Good day!

I have a question, again, of a purely academic nature, which arose as a result of a more in-depth study of the "gut" of C ++.

Suppose somewhere in the header file there is a bunch of classes of this type:

class Base{ public: Base(){ std::cout<<"Create!"<<endl; } }; class Sub1 : public virtual Base{}; class Sub2 : public Base{}; class Multi : public Sub1, public Sub2{} ; 

Here, as you can see, there is nothing more than diamond-shaped inheritance.

Next, call the constructor of the Multi class:

 int main() { Multi m; return 0; } 

Conclusion:

 Create! Create! 

Going further ... if I specify the class Sub2 as virtual, the output will be different, namely :

 Create! 

It would be very good if you clarified exactly where the constructor call was coming from the second time.

Thank!

    1 answer 1

    If I remember everything correctly, when diamond-shaped inheritance, the Multi class will have two copies of Base. But if virtual inheritance is made, the copy will be one. It is for this reason that two times displayed, then one.

    UPD: A strange, synthetic example, but maybe someone will save time.

     #include <iostream> class Base { public: int k; Base() { std::cout<<"Create!"<<std::endl; k = 0; } ~Base() { std::cout << "Delete " << k << std::endl; } }; class Sub1 : public virtual Base {}; class Sub2 : public Base {}; class Sub3 : public virtual Base {}; class Sub4 : public Base {}; class Multi : public Sub1, public Sub2, public Sub3, public Sub4 { public: Multi() { if (Sub1::k == 0) Sub1::k = 1; else std::cout << " try init 1, but was init by " << Sub1::k << std::endl; if (Sub2::k == 0) Sub2::k = 2; else std::cout << " try init 2, but was init by " << Sub2::k << std::endl; if (Sub3::k == 0) Sub3::k = 3; else std::cout << " try init 3, but was init by " << Sub3::k << std::endl; if (Sub4::k == 0) Sub4::k = 4; else std::cout << " try init 4, but was init by " << Sub4::k << std::endl; } }; int main() { Multi m; return 0; } 

    Conclusion:

     Create! Create! Create! try init 3, but was init by 1 Delete 4 Delete 2 Delete 1 

    To understand, you need to know that Sub1::k is a special form to refer to the correct branch in rhomboid inheritance (with the word I specifically got excited, of course - it works in other inheritances :)). As you can see, there are no zeros in the destructor output, so exactly all copies of Base initialized. But as you can see, one copy (number 3) was not reinitialized. Destructors are usually called in the reverse order.

    • And what does virtual inheritance mean? - gecube 7:03
    • one
      Well, as far as I know, the virtual specifier in diamond-shaped inheritance serves to ensure that the compiler selects only one of the two classes, from which real inheritance for the Multi class will occur. If this is true, then in the end the Multi class is inherited only from one of the two classes. If both Sub * classes are marked as virtual, then the compiler will merge them into one. And again, in the end there will be one constructor challenge! - Salivan
    • 2
      @gecube @Asen hint If part of the virtual ones are in the diamond-like inheritance and some are not, then it will be more fun. For those who are virtual, one object will be created, personal copies will be created for all the others. - KoVadim
    • one
      This is easier to understand if we did not have two classes in the middle (Sub1 and Sub2), but more. Let be 10. And the class Multi will be inherited from them all. In this case, five inheritances will be virtual (for Sub1 .. Sub5), and the next five (for Sub6 .. Sub10) will not. Then 6 copies of Base will be created. One copy for Sub1 .. Sub5 and a personal copy for Sub6 .. Sub10. - KoVadim 7:38
    • one
      As always, inheritance raises more problems than it solves ... Pichalka = (After all, inheritance is essentially = inclusion of a base-class in the derived-class. And, it turns out, it’s better to always use virtual inheritance instead of the usual!?!?!? - gecube 10:02