简体   繁体   中英

Virtual inheritance (diamond) - Why do I need to upcast to Base class from the most Derived class

consider the following :

#include <iostream>
#include <string>
using namespace std;


class A {
public:
   A(const char* sName) //conversion constructor
           : _sName(sName) {cout<<"(1)"<<endl;} ;
   A(const A& s) {cout<<"(2)"<<endl;} //copy constructor
   virtual ~A() {cout<<"(3)"<<endl;} //destructor
    void f1() {cout<<"(4)"<<endl; f2();} //Notice two commands!
    virtual void f2() =0;
  private:
    string _sName;
  };



  class B1: virtual public A {
  public:
     B1(const char* sAName, const char* sSName)
             : _sName1(sAName), A(sSName) {cout<<"(5)"<<endl;}
     B1(const B1& b1) : A(b1) {cout<<"(6)"<<endl;}
     ~B1() {cout<<"(7)"<<endl;}
     virtual void f1() {cout<<"(8)"<<endl;}
     virtual void f2() {cout<<"(9)"<<endl; f3();}
     virtual void f3() {cout<<"(10)"<<endl;}
  private:
     string _sName1;
  };



  class B2: virtual public A {
  public:
     B2(const char* sAName, const char* sSName)
             : _sName2(sAName), A(sSName) {cout<<"(11)"<<endl;}
     B2(const B2& b2) : A(b2) {cout<<"(12)"<<endl;}
     ~B2() {cout<<"(13)"<<endl;}
     virtual void f3() {f1(); cout<<"(14)"<<endl;}
  private:
      string _sName2;
  };

  class C: public B1, public B2 {
  public:
         C () : A(" this is A ") , B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {}
         C (const C& c) :  A(c) , B1(c) , B2(c) {}
         ~C() {cout<<"(15)"<<endl;}
         virtual void f1() {A::f1(); cout<<"(16)"<<endl;}
         void f3 () {cout<<"(17)"<<endl;}
  };


  int main() {
      /* some code */
      return 0;
  }

As you can see , I added in class C the implementation of C's Ctor (constructor) . What is not clear to me is why do I need also the upcast from C to A , if B1 does that job for me in its Ctor ? Meaning , if I wrote C's Ctor as :

C () : A(" this is A ") , B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {}

why can't I write :

C () : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {}

thanks , Ronen

In short, because it's what standard requires: you have to initialize virtual base in the ctor of the most derieved class.

A more elaborated answer, it's because you have only one base sub-object for virtual bases and this sub-object might be initialized differently in different base classes. Eg in your example


C () : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {}

What value do you expect to be passed to A ctor, " B1 " or " B2 "?

Constructor of virtual base is called before nonvirtual ansestors ctor's. In your example B1 ctor can not invoke the virtual base constructor for C as B1 ctor itself will be called later.

Because 'B1' and 'B2' both use the same memory for 'A' that 'C' does. If you didn't specify the construction of 'A' in 'C', which one of 'B1' or 'B2' should construct 'A'?

Your terminology is slightly wrong here - this is not an upcast to 'A'.

Because your class A does not have default constructor.

Compiler generates an default constructor for every class but once you explicitly overload the constructor it assumes you want to do something special and it does not generate the default constructor and when your code tries to invoke the default constructor it leads to an error.

The following code statement:

C () : B1(" this is " , " B1 ") , B2 (" this is " , " B2 ") {}

leads to calling of the class A default constructor, which is not there at all, and hence results in an error. If you provide an default constructor for your class A that should compile fine without any errors too.

Here is a quick try:

#include <iostream>

class A { public: A(int v) : m_v(v) { std::cout << "Initializing A with " << v << std::endl; } int m_v; };
class B1 : public virtual A { public: B1(int v) : A(v) {} };
class B2 : public virtual A { public: B2(int v) : A(v) {} };
class C : public B1, public B2 { public: C(int v1, int v2, int v3) : A(v1), B1(v2), B2(v3) {} };

int main()
{
    C c(1, 2, 3);

    std::cout << "c.m_v: " << c.m_v << std::endl;

    return EXIT_SUCCESS;
}

This example outputs:

Initializing A with 1
c.m_v: 1

That is, it seems that the call to A::A() is required in the most derived class because since the inheritance is virtual , it won't get called by the constructors of B1 or B2 when instantiating C .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM