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() );
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.)
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.)
No you can't, constructors can't be virtual.
[20.8] What is a "virtual constructor"? An idiom that allows you to do something that C++ doesn't directly support. You can get the effect of a virtual constructor by a virtual clone() member function (for copy constructing), or a virtual create() member function (for the default constructor).
If there is no copy constructor, c++ creates a default copy constructor which makes a shallow copy. If the object has no pointers to dynamically allocated memory then shallow copy will do.
A constructor can not be virtual because when the constructor of a class is executed, there is no virtual table in the memory, which means no virtual pointer defined yet. Hence the constructor should always be non-virtual.
You can use the curiously recursive pattern but it might make your code less readable. You will still need copy constructors. It works as follows.
struct ABC // Abstract Base Class
{
virtual ~ABC() {}
virtual ABC * clone() const = 0;
};
template <class TCopyableClass>
struct ClonableABC : public ABC
{
virtual ABC* clone() const {
return new TCopyableClass( *(TCopyableClass*)this );
}
};
struct SomeABCImpl : public ClonableABC<SomeABCImpl>
{};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With