c++classinheritancediamond-problem

Inheriting specific attributes with multiple inheritance


I've been asked to make a program which has a base class, two derived children classes from it, and then another one which is derived from both children. The assignment asks me to inherit half the attributes from one child and the rest from the other one. (Hope this makes sense to you). These attributes are not specific of the children, but of the base class. This is my code:

# include <iostream>

class Base {
    protected:
        int _var1;
        int _var2;
        int _var3;
        int _var4;

    public:

        Base() : _var1(10), _var2(20), _var3(30), _var4(40) {}
        virtual ~Base() {};

        int getVar1() const { return this->_var1; }
        int getVar2() const { return this->_var2; }
        int getVar3() const { return this->_var3; }
        int getVar4() const { return this->_var4; }
};

class Child1 : public virtual Base {
    public:
        Child1() { _var1 = 100; _var2 = 200; _var3 = 300; _var4 = 400;}
        virtual ~Child1() {}
};

class Child2 : public virtual Base {
    public:
        Child2() { _var1 = 500; _var2 = 600; _var3 = 700; _var4 = 800;}
        virtual ~Child2() {}
};

class GrandChild : public Child1, public Child2 {
    public:
        GrandChild() {}
        virtual ~GrandChild() {}
};

int main() {
    Child1      c;
    Child2      d;
    GrandChild  g;

    std::cout << c.getVar1() << std::endl;
    std::cout << d.getVar1() << std::endl;
    std::cout << g.getVar1() << std::endl;
}

With this code, g instance automatically inherits the attributes from Child2, but I want to pick which attributes it inherits from which class, e.g. _var1 from Child1 and _var3 from Child2. Is that even possible? Thank you so much in advance :)


Solution

  • I stripped the unrelated parts off your code (every thing public, and no need to declare the virtual destructor again):

    #include <iostream>
    
    struct Base {
        int var1;
        int var2;
        Base() : var1(10),var2(20) {}
        virtual ~Base() {};
    };
    
    struct Child1 : virtual Base {
        Child1() { var1 = 100; var2 = 200; }
    };
    
    struct Child2 : virtual Base {
        Child2() { var1 = 500; var2 = 600;}
    };
    
    struct GrandChild : Child1, Child2 {
        GrandChild() {}
    };
    
    int main() {
        Child1 c;
        Child2 d;
        GrandChild  g;
        std::cout << c.var1 << std::endl;
        std::cout << d.var1 << std::endl;
        std::cout << g.var1 << std::endl;
    }
    

    It produces the same output:

    100
    500
    500
    

    This is because, the Grandchild constructor is equivalent to:

    GrandChild() : Base(),Child1(),Child2() {}
    

    A virtual base is always initialized by the most derived class. Then the Child1 and Child2 subobjects are initialized by calling their constructor. They are called in this order, because thats the order here struct GrandChild : Child1, Child2.

    Thats why you see 500 on the last line of output. First, the Base constructor initializes var1 to 10. Next the Child1 constructor assigns 100. And eventually the Child2 constructor assigns 500.

    You cannot selectively inherit only some members. I suppose what you want is to let Child1 assign 100 to var1 and Child2 assign 600 to var2. Note that initialization of this members already happened when GrandChild called the constructor of Base. Because Base is a virtual base class and GrandChild is the most derived class, neither Child1 nor Child2 have any buisness in initializing the members var1 and var2 of GrandChild.

    You can however, write constructors that assign only some of the inherited members:

    #include <iostream>
    
    struct Base {
        int var1;
        int var2;
        Base() : var1(10),var2(20) {}
        virtual ~Base() {};
    };
    
    struct Child1 : virtual Base {
        Child1() { var1 = 100; }
    };
    
    struct Child2 : virtual Base {
        Child2() { var2 = 600;}
    };
    
    struct GrandChild : Child1, Child2 {
        GrandChild() : Base(),Child1(),Child2() {}
    };
    
    int main() {
        GrandChild  g;
        std::cout << g.var1 << std::endl;
        std::cout << g.var2 << std::endl;
    } 
    

    Output:

    100
    600