I have created a template class Gallery
which is intended to be used as a container for objects. I used a private member of typestd::map
to maintain the collection alongside other functions. This class was not intended to be used on production code but rather to help me practice design and coding skills. I tried to incorporate templates, Inheritance, Polymorphism, smart pointers, mutators and accessors, a few data structures, operator overloading, the big five, and exceptions. The driver Program doesn't test everything since I tried to save on space.
Be gentle, I am only a newbie.
How can this class be designed and written better?
/**
The Gallery Class can be used to hold a map of objects that has some sort of unique identifier.
The object should implement the functions:
const Identifier& getName();
std::unique_ptr<LabeledObject> clone() const;
overload: <, ==, >, and <<
*/
#ifndef GALLERY_H
#define GALLERY_H
#include <map>
#include <unordered_set>
#include <string>
#include <iostream>
#include "shape.h"
// Gallery of Objects
template<typename LabeledObject, typename Identifier> class Gallery{
public:
/// constructors
Gallery() = default;
Gallery(const Gallery& galleryB);
Gallery(Gallery&& galleryB);
/// getters
LabeledObject& objectCheckOut(const Identifier& name);
const LabeledObject& viewObject(const Identifier& name) const;
/// overloaded operators
Gallery& operator=(const Gallery& galleryB);
Gallery& operator=(Gallery&& galleryB);
bool operator<(const Gallery& galleryB) const; // compare sizes
bool operator>(const Gallery& galleryB) const; // compare sizes
bool operator==(const Gallery& galleryB) const; // compare owned objects
Gallery& operator+(const Gallery& galleryB); // append galleryB's objects
Gallery& operator-(const Gallery& galleryB); // erase objects that resemble galleryB's objects
template<typename T, typename U> friend std::ostream& operator<<(std::ostream& out, Gallery<T,U> galleryB);
/// other utility functions
void removeObject(const Identifier& name);
bool isCheckedOut(const Identifier& name) const;
bool objectExists(const Identifier& name) const;
void objectCheckIn(const Identifier& name);
void addObject(const LabeledObject& obj);
private:
// boolean to show if checked out
std::map<Identifier, std::pair<std::unique_ptr<LabeledObject>, bool>> objectsMap;
/// private exception classes
class GalleryException{
private: std::string description;
public:
GalleryException(const std::string& description = "Exception Occurred in Gallery Class")
: description(description){}
const std::string& getException() const;
};
class ObjectCheckedOut : public GalleryException{
public:
ObjectCheckedOut(const std::string& description = "Object Checked Out Exception Occurred in Gallery Class")
: GalleryException(description){}
};
class ObjectNotInGallery: public GalleryException{
public:
ObjectNotInGallery(const std::string& description = "Object Not In Gallery Exception Occurred in Gallery Class")
: GalleryException(description){}
};
class ObjectNotCheckedOut: public GalleryException{
public:
ObjectNotCheckedOut(const std::string& description = "Object Not Checked Out Exception Occurred in Gallery Class")
: GalleryException(description){}
};
/// private functions
void appendToGallery(const Gallery& galleryB);
static void handleException(GalleryException&&);
};
#endif
/************************************************************************************************
*************************************************************************************************
* FUNCTION DEFINITIONS *
* ----------------------------- *
* Note that our class is a template hence functions are best defined within the.h file *
* *
*************************************************************************************************
*************************************************************************************************/
/// constructors
/// copy constructor
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject,Identifier>::Gallery(const Gallery& galleryB){
appendToGallery(galleryB);
}
/// move constructor
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject, Identifier>::Gallery(Gallery&& galleryB)
:objectsMap(std::move(galleryB.objectsMap)){}
/// getters
/// viewObject
template<typename LabeledObject, typename Identifier>
const LabeledObject& Gallery<LabeledObject, Identifier>::viewObject(const Identifier& key) const{
if(!objectExists(key))
handleException(ObjectNotInGallery());
else if(isCheckedOut(key))
handleException(ObjectCheckedOut());
// returning reference of unique_ptr from : [Identifier, {unique_ptr, true/false}]
return *((objectsMap.find(key) -> second).first);
}
/// objectCheckOut
template<typename LabeledObject, typename Identifier>
LabeledObject& Gallery<LabeledObject, Identifier>::objectCheckOut(const Identifier& key){
if(!objectExists(key))
handleException(ObjectNotInGallery());
else if(isCheckedOut(key))
handleException(ObjectCheckedOut());
// change false to true in mapElement : [Identifier, {unique_ptr, false}]
objectsMap[key].second = true; // label as checked out
return *(objectsMap[key].first);
}
/// utility functions
/// addObject
template<typename LabeledObject, typename Identifier>
void Gallery<LabeledObject, Identifier>::addObject(const LabeledObject& obj){
// new Element is in the form [Identifier, {unique_ptr, true/false}]
objectsMap[obj.getName()] = std::make_pair(obj.clone(), false);
}
/// objectCheckIn
template<typename LabeledObject, typename Identifier>
void Gallery<LabeledObject, Identifier>::objectCheckIn(const Identifier& key){
objectsMap[key].second = false;
}
/// removeObject
template<typename LabeledObject, typename Identifier>
void Gallery<LabeledObject, Identifier>::removeObject(const Identifier& key) {
if(objectExists(key))
objectsMap.erase(key);
}
/// isCheckedOut
template<typename LabeledObject, typename Identifier>
bool Gallery<LabeledObject, Identifier>::isCheckedOut(const Identifier& key) const{
//find() returns true/false form : [Identifier, {unique_ptr, true/false}]
return objectExists(key) && (objectsMap.find(key)-> second).second;
}
/// objectExists
template<typename LabeledObject, typename Identifier>
bool Gallery<LabeledObject, Identifier>::objectExists(const Identifier& key) const{
return objectsMap.find(key) != objectsMap.end();
}
/// overloaded operators
/// assignment operator
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator=(const Gallery& galleryB){
if(objectsMap != galleryB.objectsMap){
objectsMap.clear();
appendToGallery(galleryB);
}
return *this;
}
/// move assignment
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator=(Gallery&& galleryB){
objectsMap = std::move(galleryB.objectsMap);
return *this;
}
/// == operator
template<typename LabeledObject, typename Identifier>
bool Gallery<LabeledObject, Identifier>::operator==(const Gallery& galleryB) const{
return objectsMap.size() == galleryB.objectsMap.size();
}
/// < operator
template<typename LabeledObject, typename Identifier>
bool Gallery<LabeledObject, Identifier>::operator<(const Gallery& galleryB) const{
return objectsMap.size() < galleryB.objectsMap.size();
}
/// > operator
template<typename LabeledObject, typename Identifier>
bool Gallery<LabeledObject, Identifier>::operator>(const Gallery& galleryB) const{
return objectsMap.size() > galleryB.objectsMap.size();
}
/// + operator
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator+(const Gallery& galleryB){
appendToGallery(galleryB);
return *this;
}
/// - operator
template<typename LabeledObject, typename Identifier>
Gallery<LabeledObject, Identifier>& Gallery<LabeledObject, Identifier>::operator-(const Gallery& galleryB){
for(auto it = galleryB.objectsMap.begin(); it != galleryB.objectsMap.end(); ++it)
removeObject(it -> first);
return *this;
}
/// << operator
template<typename LabeledObject, typename Identifier>
std::ostream& operator<<(std::ostream& out, Gallery<LabeledObject, Identifier> galleryB){
// it points to an element in the form : [Identifier, {unique_ptr, true/false}]
for(auto it = galleryB.objectsMap.begin(); it != galleryB.objectsMap.end(); ++it){
std::string status = (it -> second).second ? "checked out \n" :"available \n";
out << "This Object is " << status;
out << ((it ->second).first) -> clone();
}
return out;
}
/// private member functions
/// appendToGallery
template<typename LabeledObject, typename Identifier>
void Gallery<LabeledObject, Identifier>::appendToGallery(const Gallery<LabeledObject, Identifier>& galleryB){
// s is in the form : [Identifier, {unique_ptr, true/false}]
for(auto s = galleryB.objectsMap.begin(); s != galleryB.objectsMap.end(); ++s)
addObject(*((s -> second).first));
}
/// handleException
template<typename LabeledObject, typename Identifier>
void Gallery<LabeledObject, Identifier>::handleException(GalleryException&& exp){
try{
throw exp;
}catch(GalleryException){
std::cout << exp.getException();
}
}
/// functions of private member classes
/// getException
template<typename LabeledObject, typename Identifier>
const std::string& Gallery<LabeledObject, Identifier>::GalleryException::getException() const{
return description;
}
/******************************************
/* DRIVER PROGRAM
/*****************************************/
#include <iostream>
#include <vector>
#include "location.h"
#include "shape.h"
#include "circle.h"
#include "rectangle.h"
#include "gallery.h"
using namespace std;
int main() {
Location a(2,4, "A");
Location c(5,3, "C");
vector<shared_ptr<Shape>> shapes {make_shared<Shape>(c, "Tree", 35), make_shared<Circle>(c, "Circle C", 4), make_shared<Rectangle>(c, 6, 7, "Rec1")};
Gallery<Shape, std::string> g, h;
for(auto s: shapes)
g.addObject(*c);
h = g;
cout << g << h;
return 0;
}
```