C++ 示例代码:
class A {
public:
A(int) {}
};
class B : public virtual A {
public:
B(int b) : A(b) {}
};
class C : virtual public A {
public:
C(int c) : A(c) {}
};
class D : public B, public C {
public:
D() : B(1), C(2){};
};
这是钻石问题的典型代码(解决方案)。我知道为什么使用 virtual 关键字。但我不知道编译器处理该问题的内部机制。现在我遇到了关于上述机制的两种不同的理论,如下所述。
当使用 virtual 关键字继承一个类时,编译器会在派生类中添加一个虚拟基指针。我已经检查了派生类的大小,是的,它包括附加指针的大小。但我不知道在上面的示例中,当 A 类的成员在 D 类中引用时,它指向哪里以及它如何工作。
-
对于每个构造函数,编译器都会为程序员提供的每个定义创建两个版本。认识自这个链接 https://stackoverflow.com/questions/6921295/dual-emission-of-constructor-symbols例如在上面的代码中。
编译器会生成2个版本的C构造函数
C(int){} // Version1
C(int):A(int){} // Version2
以及构造函数 B 的两个不同版本
B(int){} // Version1
B(int):A(int){} // Version2
因此,当构造 D 时,编译器将生成以下代码之一
D() : B(), C(2) {} // Version1
D() : B(1), C() {} // Version2
从而确保只创建 A 的一个实例,从而避免 A 的重复副本。
请帮助我理解内部机制。
常见的用法(任何标准都没有指定!)是首先创建虚拟继承对象的实例,并将指向该实例的指针放入 vtable 中。所以这就是发生的事情:
- 创建 AN:没什么特别的
- 创建 B:构造 A 并将其链接添加到 B vtable 中
- 创建 D:首先构造 A,然后构造 B 和 C,并且每个都在其 vtable 中包含指向 A 的链接。这允许当您获得指向 D 对象的指针时,将其转换为指向 B 和 C 的指针,并且每个指针仍然知道其 A 成员在哪里。
但这只不过是对可能实现的理论答案。我并不是说实际的实现(例如 gcc、clang 或 microsoft vc)完全遵循这一点。但是,例如,如果您必须用纯 C 语言模拟虚拟继承,则可以使用它。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)