I want to perform "deep copies" of an STL container of pointers to polymorphic classes.
I know about the Prototype design pattern, implemented by means of the Virtual Ctor Idiom, as explained in the C++ FAQ Lite, Item 20.8.
It is simple and straightforward:
struct ABC // Abstract Base Class
{
virtual ~ABC() {}
virtual ABC * clone() = 0;
};
struct D1 : public ABC
{
virtual D1 * clone() { return new D1( *this ); } // Covariant Return Type
};
A deep copy is then:
for( i = 0; i < oldVector.size(); ++i )
newVector.push_back( oldVector[i]->clone() );
Drawbacks
As Andrei Alexandrescu states it:
The
clone()
implementation must follow the same pattern in all derived classes; in spite of its repetitive structure, there is no reasonable way to automate defining theclone()
member function (beyond macros, that is).
Moreover, clients of ABC
can possibly do something bad. (I mean, nothing prevents clients to do something bad, so, it will happen.)
Better design?
My question is: is there another way to make an abstract base class clonable without requiring derived classes to write clone-related code? (Helper class? Templates?)
Following is my context. Hopefully, it will help understanding my question.
I am designing a class hierarchy to perform operations on a class Image
:
struct ImgOp
{
virtual ~ImgOp() {}
bool run( Image & ) = 0;
};
Image operations are user-defined: clients of the class hierarchy will implement their own classes derived from ImgOp
:
struct CheckImageSize : public ImgOp
{
std::size_t w, h;
bool run( Image &i ) { return w==i.width() && h==i.height(); }
};
struct CheckImageResolution { ... };
struct RotateImage { ... };
...
Multiple operations can be performed sequentially on an image:
bool do_operations( vector< ImgOp* > v, Image &i )
{
for_each( v.begin(), v.end(),
/* bind2nd( mem_fun( &ImgOp::run ), i ... ) don't remember syntax */ );
}
If there are multiple images, the set can be split and shared over several threads. To ensure "thread-safety", each thread must have its own copy of all operation objects contained in v
-- v
becomes a prototype to be deep copied in each thread.
Edited: The thread-safe version uses the Prototype design pattern to enforce copy of pointed-to-objects -- not ptrs:
struct ImgOp
{
virtual ~ImgOp() {}
bool run( Image & ) = 0;
virtual ImgOp * clone() = 0; // virtual ctor
};
struct CheckImageSize : public ImgOp { /* no clone code */ };
struct CheckImageResolution : public ImgOp { /* no clone code */ };
struct RotateImage : public ImgOp { /* no clone code */ };
bool do_operations( vector< ImgOp* > v, Image &i )
{
// In another thread
vector< ImgOp* > v2;
transform( v.begin(), v.end(), // Copy pointed-to-
back_inserter( v2 ), mem_fun( &ImgOp::clone ) ); // objects
for_each( v.begin(), v.end(),
/* bind2nd( mem_fun( &ImgOp::run ), i ... ) don't remember syntax */ );
}
This has sense when image operation classes are small: do not serialize accesses to unique instances of ImgOp
s, rather provide each thread with their own copies.
The hard part is to avoid writers of new ImgOp
-derived classes to write any clone-related code. (Because this is implementation detail -- this is why I dismissed Paul's answers with the Curiously Recurring Pattern.)