Search code examples
c++classmultiple-inheritancediamond-problem

C++, child with both parents having a same ancestor


I'm having trouble with C++ classes and inheritance right now...

Let's say we have

Class A {
A(string name);
    ~A();
    void func(void);
}
Class B : public A {
    B(string name);
    ~B();
    ...
}
Class C : public A {
    C(string name);
    ~C();
    ...
}
Class D : public B, public D {
    D(string name);
    ~D();
    ...
}

Whenever I create D, it calls the constructor for B and the one for C which results in multiple instances of A. The compiler then says it doesn't know which "func" it should call, the one from B or the one from C.

I would like to know how to call A constructor ONLY ONCE and then use it to build B and C.

I already tried using B::func() to be able to call func() but has I must have a cout in the class A builder. It results in wrong output.


Solution

  • This is called the diamond inheritance pattern.
    In order to avoid having 2 instances of A in D, you need to use virtual inheritance.
    When classes e.g. B virtually inherit A, it means that A will be present only once in a class derived from those classes.
    Note: in this case it is the responsibility of the most derived class to initialize the virtual base(s) - as shown below.

    The output from the following code demonstrates it:

    #include <iostream>
    #include <string>
    
    class A {
    public:
        A(std::string const & name) { std::cout << "A::A\n"; };
        ~A() {};
        void func(void);
    };
    //--------vvvvvvv-----------
    class B : virtual public A {
    public:
        B(std::string const& name) : A(name) { std::cout << "B::B\n"; };
        ~B() {};
    };
    //--------vvvvvvv-----------
    class C : virtual public A {
    public:
        C(std::string const& name) : A(name) { std::cout << "C::C\n"; };
        ~C() {};
    };
    class D : public B, public C {
    public:
    //-------------------------------vvvvvvv---------------------------------------------
        D(std::string const& name) : A(name), B(name), C(name) { std::cout << "D::D\n"; };
        ~D() {};
    };
    
    int main()
    {
        D d("aaa");
    }
    

    Output:

    A::A
    B::B
    C::C
    D::D
    

    I.e. A is present once in D.

    Note that if you remove the virtual keyword, the output will be:

    A::A
    B::B
    A::A
    C::C
    D::D
    

    I.e. A is present twice in D (as you observed).


    Some side notes:

    1. Better to avoid using namespace std - see here Why is "using namespace std;" considered bad practice?.
    2. Your code contains many typos: Class should be class, missing ; at the end of classes.
    3. You constructors can accept the name by const & as demonstrated in my code, to avoid copy.