我理解我们如何以及为什么我们必须使用virtual
关键字来解决"钻石问题",并创建一个这样的类层次结构:
A
/
/
B C
/
/
D
代码示例:
class A { int a; public: A(int a) : a(a) {} };
class B : public virtual A { int b; public: B(int a, int b) : A(a), b(b) {} };
class C : public virtual A { int c; public: C(int a, int c) : A(a), c(c) {} };
class D : public B, public C { public: D(int a, int b, int c) : A(a), B(0, b), C(0, c) {} };
我找不到问题的答案:为什么我们必须告诉编译器(使用virtual
关键字(我们要创建一个"菱形"类层次结构?为什么编译器不自动生成它?如果我们不使用virtual
,编译器会生成下面的类层次结构:
A A
| |
| |
B C
/
/
D
是否存在第二个层次结构有用且有效的编程情况?编辑(让我的问题清楚(:他们为什么要我们使用virtual
?我想,原因是他们想给我们一个选择。任何示例,其中第二类层次结构是最佳选择?
考虑A
是访问者接口的情况,而B
和C
彼此独立地实现此接口,并且需要不同的访问行为。
class A
{
public:
virtual void visit(int) = 0;
};
class B : virtual private A
{
public:
virtual void visit(int) { }
};
class C : virtual private A
{
public:
virtual void visit(int) { }
};
class D : public B, public C { };
// error: virtual function 'A::visit' has more than one final overrider in 'D'
这不会编译。 在这种情况下,B
和C
保留单独的A
子对象是可取且必要的。 在此示例中删除虚拟继承允许它进行编译。
是否有任何编程情况,其中第二个层次结构是有用的和有效的?
虽然可能有,但在我回答了另一个问题之后,这变成了一个不那么有趣的问题(在我看来(。
为什么我们必须告诉编译器(使用 virtual 关键字(我们要创建一个"菱形"类层次结构?为什么编译器不自动生成它?
不可以,编译器无法自动生成它。 这就是为什么你必须明确地告诉编译器什么时候你想要继承是虚拟的。
假设存在这样的自动化,为了让编译器猜测B
的基A
应该是虚拟的,它必须知道A
也是C
的基础,并且B
和C
都是D
的基础。因此,B
定义的含义将取决于D
的定义(这也取决于C
.从本质上讲,属于同一层次结构的所有类都将依赖于该层次结构中的所有其他类,除了最顶层的基数(。但是层次结构的某些类可能在完全不同的翻译单元中定义,这些翻译单元的编译时间可能与其他类完全不同。C++编译器根本无法假设它了解所有类。
有一种方法可以使这成为可能:使所有继承都虚拟。但是,虚拟继承有一些(小但非零(开销,因此这不是理想的解决方案。此外,虚拟继承使得无法static_cast派生类型,因此此类强制转换需要dynamic_cast,这需要运行时类型信息,并且有兴趣允许缺乏 RTTI 支持的有限C++实现。