I'm currently writing a complicated class and in it I basically need to copy a list of derived classes. The simplified version is, as follows: I have a base class from which I derive several other classes:
class Base
{
public:
virtual void test(void)
{
cout << "Base" << endl;
}
Base(vector<Base*> *pointer)
{
pointer->push_back(this);
}
virtual Base& operator=(const Base& rhs)
{
cout << "Base=" << endl;
return *this;
}
};
class A : public Base
{
public:
void test(void)
{
cout << "A" << endl;
}
A(vector<Base*> *pointer) : Base(pointer) {}
A& operator=(const A& rhs)
{
cout << "A=" << endl;
return *this;
}
};
class B : public Base
{
public:
void test(void)
{
cout << "B" << endl;
}
B(vector<Base*> *pointer) : Base(pointer) {}
B& operator=(const B& rhs)
{
cout << "B=" << endl;
return *this;
}
};
Then I create a list of objects, which I save in the in a pointer list of the Base class:
vector<Base*> listA;
new Base(&listA);
new A(&listA);
new B(&listA);
These objects I then want to copy in a second list with the same classes (same order), but which might have different values.
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]) = (*listB[i]);
}
However c++ is not able to do that. Because the list has the type Base*, dereferencing creates an object of type Base. Therefore the assignment operator= of the Base class is called instead of the correct one from the derived class. How can I fix this?
Or how can I tell c++ to use the right operator? Maybe by some isinstanceof-function?
For a full sample see:
int main()
{
vector<Base*> listA;
new Base(&listA);
new A(&listA);
new B(&listA);
vector<Base*> listB;
new Base(&listB);
new A(&listB);
new B(&listB);
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]).test();
}
for (int i = 0; i < (int)listA.size(); i++)
{
(*listA[i]) = (*listB[i]);
}
}
Which outputs:
Base
A
B
Base=
Base=
Base=
There are a few misunderstandings here. First and foremost, what does it mean to assign an instance of a derived class to an instance of a base class? Let's take a simple hierarchy:
struct A { int x; };
struct B : A { int y; };
A a;
B b;
a = b; // what should this do?
b = a; // what about this?
With normal C++, the first one does object slicing, and the second one is ill-formed. But even the first one, well well-formed, is typically not what you want to do anyway. Are you sure you want to be slicing?
The second is that while you made your assignment operator virtual:
virtual Base& operator=(const Base& rhs)
None of the derived classes actually override it. A
's assignment operator takes an A const&
and B
's takes a B const&
. If you marked the two with override
, your compiler would point this out to you. If you fix those two to take a Base const&
argument, then you would get what you want printed - but it's probably still not what you actually want to have happen.
In order to actually make polymorphic copies, a typical solution is to provide a virtual clone method:
virtual Base* clone() const = 0;
That your derived classes implement:
struct A : Base {
A* clone() const override { return new A(*this); }
};
And then use clone()
instead of assignment. There will be no slicing here.
Insert the usual caveats about memory management and raw pointers here.