I created a container that controls the life cycle (new/delete) of certain types of objects to avoid any programming mistakes. For example, a object is deleted without any notification to the container. The objects inherit from the same base class (GreetingBase).
For the implementation I am using a “template trick”:
class GreetingBase{
public:
virtual void sayHello(){
std::cout << "Hello there!" << endl;
}
virtual ~GreetingBase(){}
};
class GreetingDerived: public GreetingBase{
public:
virtual void sayHello(){
std::cout << "Hola amigos!" << endl;
}
virtual ~GreetingDerived(){}
};
class GreetingContainer{
public:
template <class T>
void addClass(){
items.push_back(new T());
}
~GreetingContainer(){
for(std::vector<GreetingBase*>::iterator it = items.begin();
it < items.end(); it++ ){
delete *it;
}
}
void talk(){
for(std::vector<GreetingBase*>::iterator it = items.begin();
it < items.end(); it++ ){
(*it)->sayHello();
}
}
private:
std::vector<GreetingBase*> items;
};
int main(){
GreetingContainer container;
container.addClass<GreetingDerived>();
container.talk();
return 0;
}
Questions:
- Using templates in order to solve this problem is a common approach? any drawbacks?
- Any “standard way” to report better errors messages when “T” is not derived from “GreetingBase”
Thank you in advance.
I don’t know whether it’s common, but it looks sensible enough. It ensures that your container can only contain pointers to objects allocated with
new, which is good.The main problem is that your container breaks the Rule of Three. It has an implicitly generated copy constructor and copy-assignment operator which simply copy each pointer; that will give you two container objects whose destructors both try to delete the same objects.
The easiest way to fix that is by deleting these member functions, or declaring them private (with no implementation) if you’re stuck with a pre-2011 version of the language. If you need to be able to copy the container, then you’ll need to implement them to do so safely.
Personally, I’d use smart pointers rather than rolling my own RAII container;
std::unique_ptrif the container is to have exclusive ownership,std::shared_ptrif you want to share ownership, or perhapsstd::weak_ptrto hold non-owning references to objects managed elsewhere by shared pointers. If you’re stuck in the past, thenunique_ptrwon’t be available, but Boost providesshared_ptr,weak_ptr, and also Pointer containers similar to yours.In C++11, you could perhaps use a static assert, something like:
Alternatively, you might get a slightly more readable error message by creating a local pointer; then the error message at least won’t contain the full name of
push_back:The error message will be something like
can't convert Whatever* to GreetingBase*, which should be clear enough.