多重虚拟继承是否涉及虚拟函数的后期绑定式继承
Does multiple virtual inheritance involve late binding like inheritance of virtual functions?
与继承虚拟函数不同,解决虚拟继承似乎很简单,但也许我不够有创造力(狡猾?(。
虚拟继承是否与虚拟函数的继承有关?具体地说,虚拟继承会产生后期绑定吗?我看不出有什么原因。我只是因为关键词过载而怀疑。
我意识到该标准没有指定虚拟继承的实现。我对大多数非假设机器所做的一切都感兴趣,无论它们多么不完美。
正如虚拟函数涉及这些成员函数的后期绑定一样,我想您可以说虚拟继承涉及继承的数据成员的后期绑定。每个子类的内存布局可能非常不同,因此如果没有运行时类型信息,就无法解析像baseClassInstance->dataMember
这样的表达式。因此,virtual
的两种使用都需要使用"vtables"进行类特定查找。
请参阅Edsko de Vries的"多重和虚拟继承的内存布局",了解GNU编译器集群(gcc(如何实现虚拟继承,包括对象布局、结果等。据我所知,其他编译器在关键点上也类似。
虚拟继承并非没有运行时成本,但这种成本的原因不是灵活性的增加,而是模糊性的解决。
以多继承层次结构为例,其中类C
通过不同的基类继承类A
两次。对类型为C
的对象的非静态方法A::foo
的调用现在是不明确的(无论该调用是否是虚拟的(。问题是传递给成员函数的隐式this
指针。通常,每个子类在内存中的位置都是由继承层次结构唯一确定的,但在这种情况下,由于A
在C
中包含了两次,编译器必须决定如何调整成员函数调用的this
指针——这是它自己无法做到的,所以它会要求您做出决定。
这个决定更加复杂,因为我们不仅可以调用A::foo
到C
,还可以通过C
的基类。这就造成了一个困境:根据我们用来进行调用的基类,编译器会对this
指针进行不同的调整,将我们重定向到内存中A
的不同位置,这取决于我们用于调用的指针类型。事实上,对于C
的每一个实例,我们在内存中都有两个不同的A
实例。
class A {
public:
void foo();
[...]
};
class B1 : public A {};
class B2 : public A {};
class C : public B1, public B2 {};
C c;
B1* b1 = &c;
B2* b2 = &c;
//assume foo() changes some internal state of A
b1->foo();
//the state change of the previous line is not visible
//to the next call - they operate on distinct instances of A
b2->foo();
virtual
继承引入了一个额外的间接层来解决这种歧义。不是在编译时确定A
相对于其子类的位置,而是执行运行时查找。这允许编译器传递相同的内存位置调用A::foo
,无论调用是通过哪个派生类进行的。对于C
的每个实例,我们现在在内存中只有一个A
的实例。
class B1 : virtual public A {};
class B2 : virtual public A {};
[...]
C c;
B1* b1 = &c;
B2* b2 = &c;
//both calls will now operate on the same instance of A
//state changes performed by the one will be observed by the other
b1->foo();
b2->foo();
- 大小虚拟继承中的派生类
- C++ 多级虚拟继承编译问题
- 如何正确获得虚拟继承?
- 避免C++虚拟继承
- 虚拟继承基构造函数消除
- 虚拟继承中是否存在多重继承?
- 了解虚拟继承类 vtables 和 vptr 创建
- 当键是虚拟继承中涉及的基类指针时,对 std::unordered_map 项的访问崩溃
- 我是否需要在虚拟继承类的构造函数中初始化基类以解决菱形继承问题?
- C++ 不明确访问 - 虚拟继承
- 虚拟继承中来自基数的虚拟调用
- 虚拟继承:调用没有匹配函数
- 虚拟继承的内部机制
- 联合虚拟继承
- C++虚拟继承类的大小
- C++虚拟继承、虚拟析构函数和 dynamic_cast<void*>
- 虚拟继承构造函数的组装
- 虚拟继承情况下类的意外大小
- 虚拟继承情况下的 vtable
- C++解决没有虚拟继承的钻石继承问题