C++ virtual base classes: parent's copy constructor doesn't get called

1.4k views Asked by At

I have three classes as you can see in the code below. Notice how I have written the copy constructors.

#include <iostream>

class Abstract
{
public:
    Abstract(){};
    Abstract( const Abstract& other ): mA(other.mA){};
    virtual ~Abstract(){};

    void setA(double inA){mA = inA;};
    double getA(){return mA;};

    virtual void isAbstract() = 0;
protected:
    double mA;
};

class Parent : public virtual Abstract
{
public:
    Parent(){};
    Parent( const Parent& other ): Abstract(other){};
    virtual ~Parent(){};

};


class Child : public virtual Parent
{
public:
    Child(){};
    Child( const Child& other ): Parent(other){};
    virtual ~Child(){};

    void isAbstract(){};
};


int main()
{
    Child child1;
    child1.setA(5);

    Child childCopy(child1);
    std::cout << childCopy.getA() << std::endl;
    return 0;
}

Now why Abstract() gets called instead of the copy constructor Abstract( const Abstract& other ) when childCopy is being constructed?

Shouldn't Child(other) call Parent(other)? And shouldn't Parent(other) in turn call Abstract(other)?

2

There are 2 answers

0
David G On BEST ANSWER

Virtual base classes can only be initialized by the most derived class. Calls to a constructor of a virtual base from a non most-derived class are ignored and replaced with default constructor calls. This is to ensure that the virtual base subobject is initialized only once:

The correct code should place the constructor call in the most derived class' ctor-initializer:

Child(Child const& other)
    : Abstract(other) // indirect virtual bases are
                      // initialized first
    , Parent(other) // followed by direct bases
{ }
0
davidhigh On

In order to have correctly called Abstract's copy constructor, you need to specify that in the initializer list of Child's copy constructor.

Child( const Child& other ): Abstract(other), Parent(other) {};

DEMO

Here is the relevant quote from the standard, which states that the constructor of a virtual base class is called only in the most derived class. If it is missing, the default constructor is called instead (--if it exists).

§12.6.2, (13.1):

In a non-delegating constructor, initialization proceeds in the following order:

  • First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

In particular, this is why you notice Abstract's default constructor being called.

In order to avoid this pitfall, however, you can drop all user-defined copy constructors and rely on the copy constructor which is implicitly defined (which is always a good idea). DEMO 2