[英]Making (virtual) functions made innaccessible in intermediate base class accessible in most-derived class, well-defined?
[英]why are virtual base non-default constructors not called unless most-derived base explicitly invokes them?
我想了解WHY C ++標准的強制性要求,即在使用'-D_WITH_BUG_'進行編譯時,不能由中間NOT最高派生類調用虛擬基類非默認構造函數:
/* A virtual base's non-default constructor is NOT called UNLESS
* the MOST DERIVED class explicitly invokes it
*/
#include <type_traits>
#include <string>
#include <iostream>
class A
{
public:
int _a;
A(): _a(1)
{
std::cerr << "A() - me: " << ((void*)this) << std::endl;
}
A(int a): _a(a)
{
std::cerr << "A(a) - me:" << ((void*)this) << std::endl;
}
virtual ~A()
{
std::cerr << "~A" << ((void*)this) << std::endl;
}
};
class B: public virtual A
{
public:
int _b;
B(): A(), _b(2)
{
std::cerr << "B() - me: " << ((void*)this) << std::endl;
}
B(int b) : A(), _b(b)
{
std::cerr << "B(b) - me: " << ((void*)this) << std::endl;
}
B(int a, int b): A(a), _b(b)
{
std::cerr << "B(a,b) - me: " << ((void*)this) << std::endl;
}
virtual ~B()
{
std::cerr << "~B" << ((void*)this) << std::endl;
}
};
class C: public virtual B
{
public:
int _c;
C(): B(), _c(3)
{
std::cerr << "C()" << std::endl;
}
C(int a, int b, int c)
:
#ifdef _WITH_BUG_
B(a,b)
#else
A(a), B(b)
#endif
, _c(c)
{
std::cerr << "C(a,b) - me: " << ((void*)this) << std::endl;
}
virtual ~C()
{
std::cerr << "~C" << ((void*)this) << std::endl;
}
};
extern "C"
int main(int argc, const char *const* argv, const char *const* envp)
{
C c(4,5,6);
std::cerr << " a: " << c._a << " b: " << c._b << " c: " << c._c
<< std::endl;
return 0;
}
因此,當編譯時不帶-D_WITH_BUG_時,代碼將輸出:
$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \
-Wno-unused -fno-pretty-templates -Wno-register \
tCXX_VB.C -o tCXX_VB
$ ./tCXX_VB
A(a) - me:0x7ffc410b8c10
B(b) - me: 0x7ffc410b8c00
C(a,b) - me: 0x7ffc410b8bf0
a: 4 b: 5 c: 6
~C0x7ffc410b8bf0
~B0x7ffc410b8c00
~A0x7ffc410b8c10
但是當使用-D_WITH_BUG_編譯時:
$ g++ -I. -std=gnu++17 -mtune=native -g3 -fPIC -pipe -Wall -Wextra \
-Wno-unused -fno-pretty-templates -Wno-register \
-D_WITH_BUG_ tCXX_VB.C -o tCXX_VB
$ ./tCXX_VB
A() - me: 0x7ffd7153cb60
B(a,b) - me: 0x7ffd7153cb50
C(a,b) - me: 0x7ffd7153cb40
a: 1 b: 5 c: 6
~C0x7ffd7153cb40
~B0x7ffd7153cb50
~A0x7ffd7153cb60
為什么在這里必須忽略B(int a,int b)對A(a)的調用? 我了解C ++標准的要求,但是為什么呢? 什么是理性的?
如果我僅實例化一個B對象:B b(4,5); 確實得到正確的b._a值為4; 但如果B是C:C c(4,5,6)的子類,C :: a最終為1,則IFF c不會直接調用A(a)。 因此,如果B(a,b)是子類對象,則它的值與如果它是最派生對象的值是不同的。 這對我來說是非常混亂和錯誤的。 是否有希望讓足夠多的人同意在此基礎上更改C ++標准?
虛擬繼承的全部目的是解決鑽石問題 。 一旦有了虛擬基類,您的層次結構將如下所示:
A
/ \
B C
\ /
D
您需要知道何時構造A
您不能讓B
構造它,然后C
立刻覆蓋它-您需要將它構造一次。 好的,那我們什么時候可以做? 最簡單的選擇就是:讓最派生的類來做! 因此,當我們初始化D
的B
子對象時,它將不會初始化其A
子對象,因為B
不是最派生的類型。
在您的情況下,您的層次結構仍然是線性的:
A
|
B
|
C
但最派生的類型C
必須初始化所有虛擬基數A
和B
由於復雜示例中的原因, B
不會初始化其A
子對象。
此行為是由於virtual base class
。 由於A是虛擬基類,因此它是由派生程度最高的類構造的。
您可以檢查有關菱形繼承的問題以及有關類似問題的討論,以了解為什么必須采用這種方式。
首先了解虛擬基類如何解決diamod形狀問題。
class A { ...}
class B: virtual public A {...}
class C: virtual public A {...}
class D: public B, public C {...}
將基類設為虛擬時,將有一個基類對象。 中間派生類對象將全部引用相同的單個基類對象。 即,如果創建了D
對象,則B :: A和C :: A都將引用同一對象。 該單個對象是B和C的基類。因此,如果允許中間類構造基類對象,則有兩個派生類可以構造該單個對象。 通過賦予派生最多的類構造虛擬基類的責任,可以解決這種歧義。
您不太可能獲得支持來更改語言。 虛擬繼承僅在多個繼承方案中有用。
為什么在這里必須忽略B(int a,int b)對A(a)的調用?
因為已經構造了唯一的A子對象。 構造函數不是普通的函數,您不能隨便調用它。
你可以寫
C(int a, int b, int c)
: A(a), B(a, b), _c(c)
{ ... }
這將為B::B(int, int)
的主體提供傳遞給A::A(int)
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.