C++ 多重虚拟继承是否涉及虚拟函数的后期绑定式继承?

C++ 多重虚拟继承是否涉及虚拟函数的后期绑定式继承?,c++,multiple-inheritance,virtual-functions,virtual-inheritance,C++,Multiple Inheritance,Virtual Functions,Virtual Inheritance,与继承虚拟函数不同,解决虚拟继承似乎是一刀切的,但也许我不够有创造性(迂回?) 虚拟继承是否与虚拟函数的继承相关?具体来说,虚拟继承是否会导致后期绑定?我看不出有什么原因。我只是因为关键词过载而怀疑 我意识到该标准没有规定虚拟继承的实现。我对大多数非假设机器所做的任何事情都感兴趣,不管它们多么不完美。正如虚拟函数涉及到这些成员函数的后期绑定一样,我想你可以说虚拟继承涉及到继承数据成员的后期绑定。每个子类的内存布局可能完全不同,因此在没有运行时类型信息的情况下,无法解析像baseClassInst

与继承虚拟函数不同,解决虚拟继承似乎是一刀切的,但也许我不够有创造性(迂回?)

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


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

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


请参阅,以了解GNU编译器集群(gcc)如何实现虚拟继承的解释,包括对象布局、结果等。据我所知,其他编译器在关键点上是类似的。

虚拟继承并非没有运行时成本,但这一成本的原因并不是灵活性的增加,但解决一个模棱两可的问题

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

这个决定更加复杂,因为我们不仅可以通过
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)具有indirection@curiousguy您能否给出一个实际的例子,其中虚拟继承提供了除解决多继承层次结构中的上述模糊性之外的灵活性增加?例如,基类构造函数的调用可以(实际上,必须是)“overidden”在大多数派生构造函数中,构造函数调用总是在编译时解析。虚拟继承在这里没有效果。这包括从派生类构造函数到基类构造函数的调用。派生类已经知道它的所有具体基,因此不需要间接寻址。重写也在编译时解决。虚拟继承意味着构造函数是由最派生的类调用的。我们不应该称之为祖先类的后期绑定吗?@bipll:我不明白为什么,甚至不明白这意味着什么。你能详细说明一下吗?继承的数据成员不构成祖先类吗?@bipll:如果你想把“类”简单地看作是“数据成员的集合”,那么——当然,“祖先类的”等同于“继承的数据成员的”。但我不知道你为什么认为这是更好/更清楚。耸耸肩*