1

Ok heres some code.

#include <iostream>
#include <deque>
using namespace std;
class A
{
public:
    virtual void Execute()
    {
        cout << "Hello from class A" << endl;
    }
};
class B: public A
{
public:
    void Execute()
    {
        cout << "Hello from class B" << endl;
    }
};
void Main()
{
    deque<A *> aclasses = deque<A*>(0);
    deque<A *> aclasses2 = deque<A*>(0);
    A a1 = A();
    B b1 = B();
    aclasses.push_back(&a1);
    aclasses.push_back(&b1);
    aclasses[0]->Execute();
    aclasses[1]->Execute();

    //Now say I want to copy a class from aclasses to aclasses2
    //while perserving it's identity and making it a seperate entity, without
    //knowing the exact type it is.

    aclasses2.push_back(new A(*aclasses[0]));
    aclasses2.push_back(new A(*aclasses[1]));
    //Now my problem show itself
    for each(A * a in aclasses2)
        a->Execute();
    //Execute is called from the original class A both times.

}

Now you might say, why don't you just put the pointers from the first deque into the second deque? While I could but I need the data to be independent. Basically I want to be able to clone items from the first deque while preserving there identity and giving them there own data.

Now the current modified version

#include <iostream>
#include <deque>
using namespace std;
class A
{
public:
    virtual void Execute()
    {
        cout << "Hello from class A" << endl;
    }
    virtual ~A() {}             // don't forget the virtual destructor
    virtual A* clone() const {
       return new A(*this);
    }
};
class B: public A
{
public:
    void Execute()
    {
        cout << "Hello from class B" << endl;
    }
    virtual B* clone() {     // return type is co-variant
       return new B( *this );
    }
};
void MainRUNNER()
{
    deque<A *> aclasses = deque<A*>(0);
    deque<A *> aclasses2 = deque<A*>(0);
    A a1 = A();
    B b1 = B();
    aclasses.push_back(&a1);
    aclasses.push_back(&b1);
    aclasses[0]->Execute();
    aclasses[1]->Execute();

    //Now say I want to copy a class from aclasses to aclasses2
    //while perserving it's identity and making it a seperate entity, without
    //knowing the exact type it is.

    aclasses2.push_back(aclasses[0]->clone());
    aclasses2.push_back(aclasses[1]->clone());
    //Now my problem show itself
    for each(A * a in aclasses2)
        a->Execute();
    //Execute is called from the original class A both times.
}
Kelly Elton
  • 4,373
  • 10
  • 53
  • 97

4 Answers4

10

The common pattern for handling that is through a virtual clone() method in the base class that will create a new object of the appropriate type:

struct base {
    virtual ~base() {}             // don't forget the virtual destructor
    virtual base* clone() const { 
       return new base(*this); 
    }
};
struct derived : base {
    virtual derived* clone() const {     // return type is co-variant
       return new derived( *this );
    }
};
int main() {
   std::auto_ptr<base> b1( new derived );
   std::auto_ptr<base> b2( b1->clone() ); // will create a derived object
}
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I'd upvote this again, if I could, for remembering to make the destructor virtual. – David Thornley Jun 01 '11 at 20:15
  • If this works you saved me rewriting damn near a whole project. Funny thing is is I came up with the same idea, to create a virtual clone function, didn't seem to work though, I don't remember why. Only differences I see are the virtual destructor. – Kelly Elton Jun 01 '11 at 20:20
  • @kelton52: The virtual destructor is only needed for destruction of the objects in the container. Also note that it is important to know who is responsible for the deallocation of the objects if they are dynamically allocated, and that means that it is usually a bad idea to mix pointers to stack allocated objects with pointers to dynamically allocated ones... – David Rodríguez - dribeas Jun 01 '11 at 20:52
  • Is there anything else I should know? Can I inline all of these functions? – Kelly Elton Jun 01 '11 at 20:57
  • There is no reason not to inline those functions, but if you are going to call them polymorphically (as is the main purpose) whether they are declared inline or not the call will not be inlined by the compiler, as it needs to use the dynamic dispatch mechanism, i.e. the compiler does not know which of the functions it needs to call. – David Rodríguez - dribeas Jun 01 '11 at 21:19
  • Yeah I was just wondering because I implemented this into my project, and it still slices my derived class. – Kelly Elton Jun 01 '11 at 21:32
  • You should post the code that you have and does the slicing... the code as presented in the answer will not slice (it uses pointers, not values) – David Rodríguez - dribeas Jun 01 '11 at 21:39
  • I modified the code above to match your example, and that illustrates the problem still exists. – Kelly Elton Jun 01 '11 at 21:48
  • @kelton52: You are right, there was a typo in the code, the `clone()` function has to be `const` (or not `const`) in both the base class and the derived class, else they are two different functions and not an override of one another. I have corrected the code (adding the `const` to the derived version, since the function should not modify the object on which it is called. That should solve the issue. Sorry for the confusion. – David Rodríguez - dribeas Jun 01 '11 at 22:00
  • @David: Do want `=override` specifier! – Xeo Jun 01 '11 at 22:01
  • omg it worked. I thought there was no way around this. Sometimes I wished I had some kind of formal schooling for this, instead of learning the whole language from a 10 year old reference manual :p. – Kelly Elton Jun 01 '11 at 23:28
  • If I may ask, what does making the functions constant do? – Kelly Elton Jun 02 '11 at 04:33
  • @kelton53: From the semantic point of view, you promise that executing the function will not modify the object. That enables you to call the function on an object that is constant, or from which you only have a constant pointer or reference. As you set the promise in the interface, the compiler can use that information to help you catch errors: if you accidentally try to modify the object inside the member function, the compiler will catch it an tell you (it will be an error). From the implementation point of view, it means that the implicit `this` pointer is `const Type*` rather than `Type*` – David Rodríguez - dribeas Jun 02 '11 at 07:13
  • This answer was great for C++98 but it is in need of an update. `std::auto_ptr` is deprecated and `std::unique_ptr` has been added. `clone` should be `virtual std::unique_ptr clone() const;` now. – François Andrieux May 14 '21 at 16:28
3

You need to provide a virtual copy constructor – usually this is a method called clone – which is overridden in each class to return the correct type:

class A {
    virtual A* clone() {
        return new A();
    }
};

class B : public A {
    void A* clone() {
        return new B();
    }
};

The methods can of course be arbitrarily complex in order to copy the whole state.

Of course, this leaks rather a lot of memory. Use appropriate smart pointers instead of raw pointers (e.g. std::shared_ptr if your compiler supports it, boost::shared_ptr otherwise).

Konrad Rudolph
  • 530,221
  • 131
  • 937
  • 1,214
2

You have new A(...) way down there. What gets called is A's copy constructor (created implicitly by the compiler.

What you want is a clone method. See here. It recaps the appropriate item from the excellent C++ Coding Standards book. Below is a shameless copy of the final solution, which also shows a nice use of the NVI idiom to avoid the slicing problem.

class A {// …
public:
  A* Clone() const {                        // nonvirtual
    A* p = DoClone();
    assert( typeid(*p) == typeid(*this) && "DoClone incorrectly overridden" );
    return p;                                // check DoClone's returned type
  }

protected:
 A( const A& );
 virtual A* DoClone() const = 0;
};

class B : public A { // …
public:
  virtual B* Clone() const {return new B(*this); }

protected:
  B( const B& rhs ) : A( rhs ) {/* … */}
};

update A bit of an explanation. The basic idea of the clone is the same as the other excellent answers here.

Now, with cloning you have the danger of slicing objects. For example, if some object which derives from A forgets to implement its own clone method, then a call to A* a = d->clone() will not return a full D object (assuming D is a descendant of A)

The NVI idiom says to separate a public interface from a virtual interface. Thus, in this example, clone is public, but not virtual. It call a protected virtual method, doClone, which does the actual cloning, and which derived objects also implement. Because of the split, the clone method can verify that the type of the cloned object matches the type of the original object.

Community
  • 1
  • 1
Gilad Naor
  • 20,752
  • 14
  • 46
  • 53
  • I feel like this is the right answer, but I have no idea what's going on here. – Kelly Elton Jun 01 '11 at 21:54
  • Could you please explain how NVI avoids slicing? Of ourse using NVI is good for other reasons but I don’t think it helps with slicing. – Konrad Rudolph Jun 02 '11 at 09:35
  • Using the NVI idiom, the base class's clone can perform actions before/after the virtual clone operation. In this example, it perform a (runtime) check that the derived class remembered to implement the clone method. If used with David's initial solution, then the assert would have caught that the derived class didn't implement the correct clone method (i.e. const). – Gilad Naor Jun 02 '11 at 10:07
  • @kelton52 for the full background, see some of the links. I will modify the answer to explain it briefly. – Gilad Naor Jun 02 '11 at 10:10
  • @Konrad also should mention that the copy ctor is not public. The assignment operation should also be non-public. Thus, if I'm not mistaken, slicing can only occur if a derived object forgets to implement the clone method. (or, if the cctor & operator= are protected, a derived object can misuse them) – Gilad Naor Jun 02 '11 at 10:20
-1

I think you confuse classes with objects, i.e. instances of those classes.

Your container aclasses store pointers to existing objects. You may take the same pointer and push it several times in many different containers, this is not called cloning.

valdo
  • 12,632
  • 2
  • 37
  • 67