![](/img/trans.png)
[英]in solution of diamond problem, why we need to inherit grand parent class two time virtually?
[英]In a diamond problem in c++ , Why do we need to call grand_parent constructor from child class?
請閱讀代碼以了解情況。
#include <iostream>
using namespace std;
class one
{
protected:
int x;
public:
one(int a)
{
x=a;
cout << "one cons called\n";
}
void display(void)
{
cout << "x = " << x << endl;
}
~one()
{
cout << "one destroy\n";
}
};
class two : virtual protected one
{
protected:
int y;
public:
two(int a,int b) : one(a),y(b)
{
cout << "two cons called\n";
}
void display(void)
{
one::display();
cout << "y = " << y << endl;
}
~two()
{
cout << "two destroy\n";
}
};
class three : protected virtual one
{
protected:
int z;
public:
three(int a,int b) : one(a),z(b)
{
cout << "Three cons called\n";
}
void display(void)
{
one::display();
cout << "z = " << z << endl;
}
~three()
{
cout << "three destroy\n";
}
};
class four : private two, private three
{
public:
four(int a,int b,int c) :one(a), two(a,b),three(a,c)
{
cout << " four cons called\n";
}
void display(void)
{
one::display();
cout << "y = " << y << endl;
cout << "z = " << z << endl;
}
~four()
{
cout << "four destroy\n";
}
};
int main()
{
four ob(1,2,3);
ob.display();
return 0;
}
如果我替換代碼
four(int a,int b,int c) :one(a), two(a,b),three(a,c)
同
four(int a,int b,int c) :two(a,b),three(a,c)
一個錯誤消息:在我的codeblock ide中沒有用於調用'one :: one()的匹配函數。
正如您所看到的,這是一個基於鑽石問題的代碼。第一類是grand_parent類。 第二和第三類作為父類,第四類作為子類。 所以我使用虛擬關鍵字來避免歧義。 除非有一件事我在這里理解的一切。我知道當父類具有參數化構造函數時,我們需要從派生類提供該構造函數的參數。 那么為什么需要為構造函數提供參數,其中類4只有2個父類,即2和3。 如果我不從第四類調用構造函數1,代碼將給出編譯時錯誤。 請解釋我為什么需要這樣做。
該virtual
層次結構中的繼承歧義消除的基類的存在, one
通過確保只有一個單一實例one
存儲在子類two
或three
。 回想一下,繼承一些類時,派生實例將始終存儲基本實例somehere內部-所以virtual
繼承確保的情況下, one
內部的two
和three
是有點“重寫”的任何類進一步下跌的繼承層次。
現在的問題是:誰是負責初始化這一個單一的one
實例? 它應該是two
還是three
? 顯然不是兩者都有,因為只有一個例子。 在這里你是:它總是最派生的類負責初始化one
- 這是有道理的:嵌入基類副本的實例必須初始化它。
這就是具有嵌入式基類實例的類層次結構在沒有four
和four
加上virtual
繼承的情況下的樣子:
+----------+ +----------+
| one | | one |
+----+-----+ +----+-----+
| |
| |
+-------+-----------+ virtual +--------+--------+ virtual
| | | |
| | | |
+--------+-------+ +-------+-------+ +----+----+ +----+----+
| two | | three | | two | | three |
| +------------+ | | +----------+ | +----+----+ +----+----+
| | one | | | | one | | | |
| +------------+ | | +----------+ | +--------+--------+
| => must init! | | => must init! | |
+----------------+ +---------------+ +-------+--------+
| four |
| +------------+ |
| | one | |
| +------------+ |
| => must init! |
+----------------+
您可以通過這種方式考慮這種機制: virtual
繼承為基類實例提供virtual
,包括構造實例 - 此責任性在層次結構中傳遞。
說你有以下鑽石:
Base
/ \
Left Right
\ /
Down
Base
類可以非常簡單,它有一個由構造函數初始化的int
成員:
struct Base
{
Base(int x)
: x(x)
{}
virtual ~Base() = default;
int x;
};
由於Left
繼承自Base
,因此其構造函數可以將參數傳遞給Base
構造函數。 在這里,如果構造一個Left
對象,其x
成員將為1
:
struct Left : virtual Base
{
Left() : Base(1)
{}
};
另一個類Right
也繼承自Base
。 這意味着它的構造函數也可以將參數傳遞給Base
構造函數。 這里,它的x
成員將是2
:
struct Right : virtual Base
{
Right() : Base(2)
{}
};
現在來到有趣的部分:如果你從Left
和Right
繼承會發生什么?
// This does not compile.
struct Down : Left, Right
{
Down() : Left(), Right()
{}
};
Left
和Right
調用Base
構造函數,但它們使用不同的參數。 編譯器現在應該使用Left
的Base(1)
部分還是應該使用Right
的Base(2)
部分? 答案很簡單:它既不使用! 編譯器將選擇留給您,並允許您指定應使用哪個構造函數:
// Hooray, this version compiles.
struct Down : Left, Right
{
Down() : Base(42), Left(), Right()
{}
};
當一個類的兩個超類具有公共基類時,就會發生Diamond問題。 此問題的解決方案是“虛擬”關鍵字。 一般情況下,不允許直接調用祖父母的構造函數,必須通過父類調用它。 只有在我們使用“Virtual”關鍵字時才允許它。 因此,當我們使用'virtual'關鍵字時,即使父類顯式調用參數化構造函數,默認情況下也會調用祖父類的默認構造函數。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.