簡體   English   中英

虛擬繼承(鑽石)-為什么需要從派生程度最高的類上轉換為基類

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

考慮以下 :

#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;
  }

如您所見,我在class C添加了C的Ctor(構造函數)的實現。 對我來說不清楚的是,如果B1在其Ctor中替我完成了工作,為什么我也需要從C到A的上行? 意思是,如果我將C的Ctor寫成:

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

我為什么不能寫:

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

謝謝羅尼

簡而言之,因為這是標准的要求:您必須在最受剝奪的類的ctor中初始化虛擬基數。

一個更詳盡的答案是,因為您只有一個用於虛擬基礎的基礎子對象,並且此子對象可能在不同的基礎類中以不同的方式初始化。 例如在您的示例中


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

您希望將什么值傳遞給“ b1”或“ b2”?

虛擬基礎的構造函數非虛擬anstors ctor 之前被稱為。 在您的示例中,B1 ctor無法調用C的虛擬基本構造函數,因為B1 ctor本身將在以后被調用。

因為“ B1”和“ B2”都使用與“ C”相同的“ A”存儲空間。 如果未在“ C”中指定“ A”的構造,那么“ B1”或“ B2”中的哪一個應構造“ A”?

您的術語在這里有點錯誤-這不是對'A'的偏up。

因為您的class A沒有默認構造函數。

編譯器會為每個類生成一個默認的構造函數,但是一旦您顯式重載該構造函數,它就會假定您要執行特殊的操作,並且不會生成默認的構造函數,並且當您的代碼嘗試調用默認的構造函數時,它將導致錯誤。

以下代碼語句:

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

導致調用類A的默認構造函數,該構造函數根本不存在,因此導致錯誤。 如果您為class A提供了默認構造函數,則該構造函數也可以正常編譯而不會出現任何錯誤。

這是一個快速嘗試:

#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;
}

此示例輸出:

Initializing A with 1
c.m_v: 1

也就是說,似乎在最派生的類中需要對A::A()進行調用,因為由於繼承是virtual ,因此在實例化CB1B2的構造函數不會調用它。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM