Reputation: 1297
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
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
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
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
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