在大多数书籍和文章中,进行多重继承的唯一“安全”(或至少是唯一建议的)方式是使用纯抽象基类(可以称为虚拟接口)的虚拟继承。
原因主要是为了避免钻石问题,人们可能会对数据成员的价值或非纯虚函数的实现状态产生歧义。
纯抽象基类不受两者的影响(没有数据成员,没有非纯虚拟),虚拟继承甚至解决了基类的实际内存地址的模糊性。
但是给出了这样的解释:如果歧义只来自“状态”的形式(例如数据成员,静态函数变量),那么就不是无状态的非抽象(甚至可能是所有“最终”方法)类同样安全的是多继承层次结构中的基类?
我错过了什么可能的问题?
PS:如果答案是“如果没有虚拟方法,那么你可以使用组合”:除了学术兴趣,我有一个案例,我需要成员函数的属性,以便能够自由阴影,全局C-样式函数,所以我无法通过指向合成对象的指针来访问它们。
虚拟继承已经通过避免非静态成员的重复副本实现了所需的安全性。在这方面,变量和函数是相同的:即使基类是无状态的,如果它是一个基类不止一次,它的非静态成员函数也是不明确的。
虚拟继承还可以智能地处理覆盖:
struct B {
virtual ~B()=default;
virtual void f()/*=0*/;
};
struct X : virtual B {};
struct Y : virtual B {
void f() override;
};
struct D : X,Y {};
B& b();
void g() {
b().f(); // calls Y::f
}
B::f
是否是纯粹的虚拟并不重要。
所以无国籍不是重要的部分。此外,如果基数是无状态的,那么使用final
成员将阻止默认存根实现的最明显的用例。 (唯一的另一种可能性是依赖于this
的值;否则函数可能是静态的。)所以我不一定鼓励使类无状态,因为它用于虚拟继承(而不仅仅是因为最小化状态是一般都是好主意)。
所以你是正确的,非纯虚函数可以是安全的,但即使基数是有状态的,这也是正确的。当然,如果重载集接受多个直接基础,那么多重继承的安全性仍然存在限制。