2

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=
jan.sende
  • 750
  • 6
  • 23
  • Possible duplicate of [Why doesn't a derived class use the base class operator= (assignment operator)?](http://stackoverflow.com/questions/10838211/why-doesnt-a-derived-class-use-the-base-class-operator-assignment-operator?rq=1) – πάντα ῥεῖ Jul 31 '16 at 16:12
  • What does it mean to assign an `A` to a `B` or vice-versa? – aschepler Jul 31 '16 at 16:33

2 Answers2

4

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.

Community
  • 1
  • 1
Barry
  • 286,269
  • 29
  • 621
  • 977
  • Thanks! I understand what you are saying. Only the last part I can't wrap my head around. How would I use this clone function in practice? – jan.sende Jul 31 '16 at 17:00
  • You mean by completely replacing the old object? Unfortunately that is not an option! – jan.sende Jul 31 '16 at 17:07
0

Okay. I found a solution for my problem. I implemented a copy function which takes the Base class as the argument. Inside this copy function I can copy the variables using the pointa. The classe now are as follows:

class Base
{
public:
    virtual void test(void)
    {
        cout << "Base" << endl;
    }
    Base(vector<Base*> *pointer)
    {
        pointer->push_back(this);
    }
    virtual void clone(Base* pointer) = 0;
};
class A : public Base
{
public:
    void test(void)
    {
        cout << "A" << endl;
    }
    A(vector<Base*> *pointer) : Base(pointer) {}
    void clone(Base* pointer) override
    {
        A* pointa = (A*)pointer;
        cout << "clone A" << endl;
        //Clone Variables here
    }
};
class B : public Base
{
public:
    void test(void)
    {
        cout << "B" << endl;
    }
    B(vector<Base*> *pointer) : Base(pointer) {}
    void clone(Base* pointer) override
    {
        B* pointa = (B*)pointer;
        cout << "clone B" << endl;
        //Clone Variables here
    }
};

This means I can now copy the objects in the following way:

for (int i = 0; i < (int)listA.size(); i++)
{
    listA[i]->clone(listB[i]);
}

However this solution is not in any way typesafe, a requirement I want to meet. I looked into my idea, and decided to do things manually without the list, which means lot's of duplicated code, but brings peace of mind.

jan.sende
  • 750
  • 6
  • 23