【问题标题】:Does multiple virtual inheritance involve late binding like inheritance of virtual functions?多重虚拟继承是否涉及后期绑定,如虚拟函数的继承?
【发布时间】:2014-07-01 07:08:35
【问题描述】:

与继承虚函数不同,解决虚继承似乎很简单,但也许我不够有创意(狡猾?)。

虚拟继承是否与虚拟函数的继承有关?具体来说,虚拟继承会导致后期绑定吗?我看不出有什么理由。我只是因为关键字重载而怀疑。

我意识到标准没有指定虚拟继承的实现。我对大多数非假设机器所做的任何事情都感兴趣,无论多么不完美。

【问题讨论】:

  • 嗯,至少不明白为什么它不被切割和干燥。

标签: c++ multiple-inheritance virtual-functions virtual-inheritance


【解决方案1】:

正如虚函数涉及这些成员函数的后期绑定,我想你可以说虚继承涉及继承的数据成员的后期绑定。每个子类的内存布局可能完全不同,因此如果没有运行时类型信息,就无法解析像 baseClassInstance->dataMember 这样的表达式。因此,virtual 的两种用法都需要使用“vtables”进行特定于类的查找。

请参阅"Memory Layout for Multiple and Virtual Inheritance", by Edsko de Vries,了解 GNU 编译器集群 (gcc) 如何实现虚拟继承,包括对象布局、结果等。据我所知,其他编译器在关键点上是相似的。

【讨论】:

  • 我们不应该称之为祖先类的后期绑定吗?
  • @bipll:我不明白为什么,甚至不明白这意味着什么。你能详细说明一下吗?
  • 继承的数据成员不构成祖先类吗?
  • @bipll:如果您想将“类”仅仅视为“数据成员的集合”,那么——当然,“祖先类的”等同于“继承的数据成员的” .但我不确定你为什么认为这更好/更清晰。 *耸耸肩*
【解决方案2】:

虚拟继承并非没有运行时成本,但是这种成本的原因不是灵活性的增加,而是歧义的解决。

以多重继承层次结构为例,其中类C 通过不同的基类两次继承类A。对 C 类型的对象的非静态方法 A::foo 的调用现在是不明确的(无论该调用是否是虚拟的)。问题在于传递给成员函数的隐式this 指针。通常,每个子类在内存中的位置由继承层次唯一确定,但在这种情况下,由于C中包含两次A,编译器必须决定如何调整成员函数的this指针call - 它不能自己做,所以它会要求你决定。

这个决定更加复杂,因为我们不仅可以通过C 调用A::foo,还可以通过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();

【讨论】:

  • "这个成本的原因不是灵活性的增加" 1) 虚拟提供了灵活性的增加 2) 具有间接性
  • @curiousguy 你能举一个实际的例子,说明虚拟继承除了解决前面提到的多继承层次结构中的歧义之外,还增加了灵活性?
  • 例如,基类构造函数的调用可以(实际上,必须)“覆盖”在最派生的构造函数中。
  • @curiousguy 构造函数调用总是在编译时解析。虚拟继承在这里不起作用。这包括从派生类构造函数到基类构造函数的调用。派生类已经知道它的所有具体基类,因此不需要间接。
  • 覆盖也在编译时解决。虚拟继承意味着构造函数被最派生的类调用。
猜你喜欢
  • 2017-03-31
  • 2015-11-21
  • 1970-01-01
  • 1970-01-01
  • 2014-10-12
  • 2015-06-21
  • 2013-11-19
  • 1970-01-01
  • 2016-10-26
相关资源
最近更新 更多