Search code examples
c++copy-constructordefault-constructor

Why operator= and copy constructor are treated differently in virtual inheritance?


It seems that in virtual inheritance, operator= and copy constructor are treated differently. Consider the following code:

#include <iostream>
#include <ostream>

class A {
public:
    A(int x) : x(x) {
        std::cout << "A is initialized" << std::endl;
    }

    A(const A& rhs) {
        std::cout << "Copy constructor for A" << std::endl;
    }

    A& operator=(const A& rhs) {
        std::cout << "A::operator=()" << std::endl;
        return *this;
    }

    virtual void funcB() = 0;
    virtual void funcC() = 0;
    int x;
};

class B : virtual public A {
public:
    B(int x) {
        std::cout << "B is initialized" << std::endl;
    }
    B(const B& rhs) {
        std::cout << "Copy constructor for B" << std::endl;
    }
    B& operator=(const B& rhs) {
        std::cout << "B::operator=()" << std::endl;
        return *this;
    }

    void funcB() override {
        std::cout << "B" << std::endl;
    }
    void funcC() override = 0;
};

class C : public B {
public:
    C(int x) : A(x + 1), B(x) {
        std::cout << "C is initialized" << std::endl;
    }
    void funcC() override {
        std::cout << "C" << std::endl;
    }
};

int main() {
    C c(1);
    C c2(c);
    c2 = c;
    std::cout << c.x;
}

Here B inherit virtually from A and C inherit from B. The output is:

A is initialized
B is initialized
C is initialized
Copy constructor for A
Copy constructor for B
B::operator=()
2

We can see that the default copy constructor of C has successfully called the copy constructor for both B and A, which is what I want. But the default operator= did not call operator= of A, which is strange.

A possible explanation to this is that the copy constructor of A is called by B, not C. However, since I have deliberately made B pure virtual, I don't have to initialize A in the copy constructor of B and in fact I did not. So the copy constructor of A is called most likely from C, but I have no proof of it since A will be initialized before B anyway, no matter who calls its constructor.


Solution

  • you're using compiler generated operator= (i.e C::operator=(const C&)), which calls operator= for all it's direct base class (and members)

    since A is not a direct base class of C, A::operator=(const A&) is not called.

    B is expected to copy A if it want, unlike constructor, you can implement assignment for B that doesn't change it's A (or whatever you want)


    on the other hand, if A is direct base class

    class C : public B , virtual public A { ... }
    

    then the compiler generated operator= would call A's assignment operator (and it's unspecific whether A is assigned multiple times)