mmm
mmm

Reputation: 1297

Polymorphic copy-constructor with type conversion

I need to copy-construct an object simultaneously changing it's type to another class being a member of the same class-hierarchy. I've read about polymorphic copy-constructors and (hopefully) understand the idea behind it. Yet, I still don't know if this pattern applies to my case and, if so, how to implement it. I think it's best if I show what I need on an example.

There is a Base class and two child classes, Child1 and Child2. I need to create an object of type Child2 basing on Child1, ie. most of all, I need to copy the object p_int is pointing to from Child1 to Child2. I've written a simple program to illustrate it:

#include <iostream>
using namespace std;

class Base {
public:
    Base() { p_int = new int; *p_int = 0; }
    ~Base() { delete p_int; }
    virtual Base* clone() const = 0;

    void setpInt(int val) { *p_int = val; }
    void setInt(int val) { a = val; }
    virtual void print() {
        cout << "Base: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
protected:
    int* p_int;
    int a;
};

class Child1 : public Base {
public:
    Child1() {};
    Child1(const Child1& child) {
        p_int = new int (*child.p_int);
        a = child.a + 1;
    }

    Base* clone() const { return new Child1(*this); }

    void print() {
        cout << "Child1: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
};

class Child2 : public Base {
public:
    Child2() {};
    Child2(const Child2& child) {
        p_int = new int (*child.p_int);
        a = child.a + 1;
    }

    Base* clone() const { return new Child2(*this); }

    void print() {
        cout << "Child2: ";
        cout << (long)p_int << ":" << *p_int << " " << a << endl;
    }
};

int main() {
    Child1* c1 = new Child1();
    Child2* c2;

    c1->setpInt(4);
    c1->print();

    c2 = (Child2*)c1->clone();
    c2->print();
}

Unfortunately, the outcome is as below, ie. there is no type conversion:

Child1: 162611224:4 0
Child1: 162611272:4 1

What exactly do I need to implement, to be able to achieve what I need? I'm starting to think there is a type-conversion mechanism I need to implement rather than a polymorphic copy-constructor, but I'm confused already.

EDIT: Asked a follow up here

Upvotes: 0

Views: 1396

Answers (4)

MichalR
MichalR

Reputation: 263

The clone() patterns allows you to create a valid copy/clone of the object of a child class having just the base reference, e.g. in your case it allows you to do the following:

Base* basePtr = getBaseOrSomeDerivedObject();
Base* copy = basePtr.clone(); // Create a copy that is an object of an actual basePtr's type.

What you could need is a "copy-constructor" that allows you to copy from a base class, e.g.:

class Base {
public:
    // [...]    
    Base(const Base& other) : a(other.a + 1)
    {
        p_int = new int(*(other.p_int));
    }
    // [...]
};


class Child2 : public Base {
public:
    // [...]
    Child2(const Base& base) : Base(base) {}
    // [...]
};

int main() {
    // [...]
    c2 = new Child2(*c1);
    c2->print();
}

Result:

Child1: 7275360:4 0
Child2: 7340936:4 1

Upvotes: 2

imre
imre

Reputation: 1717

If you only have 2 child classes, then the easiest way is to create a conversion constructor:

class Child2: public Base
{
public: 
    Child2(Child1 const& child)
    {
            p_int = new int (*child.p_int);
            a = child.a + 1;        
    }
}; 

c2 = new Child2(*c1); 

If you have several Child classes, and you need to create a Child2 from any of them, then you could do something like this:

class Base
{
public: 
    void CopyFrom(Base* base)
    {
            p_int = new int (*base.p_int);
            a = base.a + 1;     
    }
}; 

class ChildX: public Base
{
public: 
    static ChildX* CreateFrom(Base* base)
    {
        ChildX ch = new ChildX(); 
        ch->CopyFrom(base); 
        return ch; 
    }
}; 

c2 = Child2::CreateFrom(c1); 

Upvotes: 4

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361532

c2 = (Child2*)c1->clone();

Here is a serious bug, and the c-style cast hides the bug.

If you use C++-style cast, then it will not hide the bug, and you will know it. In this case, the C++-style cast is : dynamic_cast. Use it to discover the bug yourself.

As it is clear from the code thatc1-clone() creates a clone of c1 whose type is Child1* and clone() returns a pointer of type Base* (after upcasting from Child1*), which you're trying to down-cast to Child2*. The cast should fail if you use proper cast : dynamic_cast.

Upvotes: 2

Chowlett
Chowlett

Reputation: 46667

Simplest solution would probably be to implement a Child2 constructor taking a Child1& as parameter. Then you could simply call:

Child2* c2 = new Child2(*c1);

Upvotes: 5

Related Questions