Home
CodeBlog
Articles
Downloads
Links
Books
About
|
Websites |
Comparing two approaches to implement polymorphic collectionsGenerally to implement polymorphic collections is necessary to create containers of pointers.Using STL containers directly can be dangerous because, for instance, each time you want to remove an element you would worry about to remove and delete it. One solution is to use smart pointers but this solution has a cost. On the other hand, the raw pointer implementation requires few lines more to be implemented. You can see the implementations here and compare. Both are equally exception safe code. Common code for both: //our polimorphic item class Shape { public: virtual Shape * clone() const = 0; }; class Circle : public Shape { Shape * clone() const { return new Circle(*this); } };Raw pointer implementation of Shapes struct delete_f { template <class T> void operator()(T *p) { delete p; } }; class Shapes { typedef vector<Shape*> ShapesVec; ShapesVec shapes; public: Shapes(){} Shapes(const Shapes &other) { Shapes temp; ShapesVec::const_iterator it = other.shapes.begin(); for ( ;it != other.shapes.end(); ++it) { temp.add((*it)->clone()); } shapes.swap(temp.shapes); } ~Shapes() { std::for_each(shapes.begin(), shapes.end(), delete_f()); } void add(Shape* p) { std::auto_ptr<Shape> sp(p); shapes.push_back(sp.get()); sp.release(); } };Sample of use
Shapes shapes;
shapes.add(new Circle);
Shared_ptr pointer implementation of Shapes
class Shapes { typedef vector< std::tr1::shared_ptr<Shape> > ShapesVec; ShapesVec shapes; public: Shapes() { } Shapes(const Shapes &other) { ShapesVec::const_iterator it = other.shapes.begin(); for ( ;it != other.shapes.end(); ++it) { add(std::tr1::shared_ptr<Shape>((*it)->clone())); } } ~Shapes() { } void add(std::tr1::shared_ptr<Shape> p) { shapes.push_back(p); } };Sample of use
Shapes shapes;
shapes.add(std::tr1::shared_ptr<Shape>(new Circle));
The copy constructor has been implemented in this sample. However, in many cases it is not necessary. Comparing the implementations we can notice that only few lines are required to make the raw pointer container safe.
The destructor:
~Shapes()
{
std::for_each(shapes.begin(), shapes.end(), delete_f()); //extra
}
and the add function:
void add(Shape* p) { std::auto_ptr<Shape> sp(p); //extra shapes.push_back(sp.get()); sp.release(); //extra }or void add(std::auto_ptr<Shape> &sp) { shapes.push_back(sp.get()); sp.release(); //extra }
|