1

Given a pointer to an abstract base class A*, I want to copy or assign it (as the base class) and have the derived copy constructor or assignment operator called. I understand copy constructors cannot be virtual, so presumably the copy constructor isn't an option for doing this, but the assignment operator is. Still, it doesn't seem to work: the following code prints

assigned a
x!
destroyed b
destroyed b

which fails to assign b.

#include <iostream>
using namespace std;

class A
{
public:
    virtual void x()=0;
    virtual ~A() {}
    virtual A& operator=(const A& other) { cout << "assigned a" << endl; return *this;}
};

class B : public A
{
public:
    virtual B& operator=(const B& other)  { cout << "assigned b" << endl; return *this;}
    virtual void x() { cout << "x!" << endl; }
    virtual ~B() { cout << "destroyed b" << endl; }
};

int main() 
{ 
    A* a = new B(); 
    A* aa = new B(); 
    *aa=*a; 
    aa->x(); 
    delete a; 
    delete aa; 
    return 0;
}

How to do this?

EDIT this question has been correctly answered below, but it was the wrong question. I shouldn't try to override the assignment operator because I don't want subclasses of A to assign to one another. For the simpler answer (hopefully) see C++ elegantly clone derived class by calling base class

Community
  • 1
  • 1
Sideshow Bob
  • 4,566
  • 5
  • 42
  • 79
  • Not that it matters much, but the question asks about a **virtual base class** and the code does not have one. – Pete Becker Mar 23 '16 at 14:37
  • I think you mean *abstract* base class, not "virtual" (which is a completely different thing). The abstractness of the base class is irrelevant to your problem. – molbdnilo Mar 23 '16 at 14:38

1 Answers1

4

The problem is that your B::operator= does not override the one in A. Change it to

virtual A& operator=(const A& other)  { cout << "assigned b" << endl; return *this;}

and it will work. Also, try to use the override keyword when overriding member functions (requires C++11). The code won't compile if you don't override. In your case, it would have caught your mistake

error: 'virtual B& B::operator=(const B&)' marked 'override', but does not override

PS: you were probably thinking about covariant return types. In order for it to work, the signature of your function has to be the same, except the return type. E.g., this will work:

virtual B& operator=(const A& other)  { cout << "assigned b" << endl; return *this;}
Community
  • 1
  • 1
vsoftco
  • 55,410
  • 12
  • 139
  • 252
  • Ok that's good, but how would `B::operator=` access fields unique to `B` if it cannot guarantee that `const A& other` is an instance of `B`? – Sideshow Bob Mar 23 '16 at 14:42
  • @SideshowBob You can test the result of a [`dynamic_cast`](http://en.cppreference.com/w/cpp/language/dynamic_cast) to see if `other` is really a `B`. But this should also make you think whether your design is sound. – vsoftco Mar 23 '16 at 14:43
  • The fact this is hard is indeed making me question my design. Ultimately, I need to deep-copy a class from a base class pointer without knowing what the class really is. Under the principle of encapsulation that's not an insane requirement, right? – Sideshow Bob Mar 23 '16 at 14:48
  • 1
    @SideshowBob One solution is to use [double dispatching](https://en.wikipedia.org/wiki/Double_dispatch#Double_dispatch_in_C.2B.2B) via a different function, call it `assign`. – vsoftco Mar 23 '16 at 14:52
  • That means giving all the derived classes an `assign` function (or `clone` would do the job in my case) when only one of them needs anything other than the default. Is there a way to say `find the derived class assignment operator (which will be virtual if not default) and use it`? – Sideshow Bob Mar 23 '16 at 14:56
  • @SideshowBob I think you can have a base class with a virtual `operator=`, then implement double dispatching with it. Basically you use `operator=` as you'd use `assign`. Scott Meyers has a very nice introduction to double dispatching in *More effective C++, Item 31*. – vsoftco Mar 23 '16 at 15:38