2011-09-01 79 views
2

考慮以下上溯造型到基類:虛擬繼承(鑽石) - 做我爲什麼需要從最派生類

#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到A的倒戈,如果B1在Ctor爲我做這項工作? 意義,如果我寫的C'S構造函數爲:

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

我爲什麼不能寫:

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

感謝, 羅南

回答

2

虛擬基地的構造者被稱爲 nonvirtual ansestors ctor's。在你的例子中,B1 ctor不能調用C的虛擬基類構造函數,因爲B1 ctor本身稍後會被調用。

+0

即使第二種格式可以工作,只要在抽象類A中存在缺省構造函數即可。缺少該缺省構造函數是第二種格式不能編譯的真正原因,請參閱我的答案。 –

+0

@Als:我同意你的回答,但是沒有任何矛盾。虛擬基礎ctor被大多數派生類ctor調用。 Base Ctor可能是默認的或不是。在這種情況下,簡單地忽略中間ctor中的虛擬基礎調用 – user396672

1

因爲 'B1' 和 'B2' 都使用與'C'相同的'A'記憶。如果你沒有在'C'中指定'A'的構造,'B1'或'B2'中哪一個應該構造'A'?

你的術語在這裏有點不對 - 這不是對'A'的傾倒。

+0

好的,那麼如果沒有upcast,那叫什麼呢? –

+1

構造函數調用 – Pete

2

簡而言之,因爲它是標準要求的:你必須在最懶惰的類的ctor中初始化虛擬基礎。

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

 

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

你期望將什麼值傳遞給Ctor,「B1」或「B2」?

1

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

編譯器爲每個類生成一個默認的構造函數,但是一旦你明確地重載了​​構造函數,它假定你想做一些特殊的事情,並且它不會生成默認的構造函數,並且當你的代碼試圖調用默認的構造函數時會導致錯誤。

下面的代碼聲明:

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

導致調用類默認構造函數,這是根本不存在的,因此會導致錯誤。如果您爲您的class A提供默認構造函數,那麼compile fine也不會出現任何錯誤。

1

這裏是一個快速嘗試:

#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()最派生類中是必需的,因爲自從繼承當實例化C時,B1B2的構造函數不會調用它。