Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ Virtual Constructor, without clone()

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 the clone() 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 ImgOps, 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.)

like image 279
Julien-L Avatar asked May 04 '10 14:05

Julien-L


People also ask

Can we have virtual copy constructor?

No you can't, constructors can't be virtual.

Can we define virtual constructor?

[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).

What happens if there is no copy 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.

Why there is no Virtual constructor?

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.


1 Answers

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>
{};
like image 107
fulmicoton Avatar answered Oct 20 '22 10:10

fulmicoton