C++ 什么时候虚拟继承是一个好的设计?

C++ 什么时候虚拟继承是一个好的设计?,c++,multiple-inheritance,virtual-inheritance,C++,Multiple Inheritance,Virtual Inheritance,EDIT3:请确保在回答之前清楚地理解我的问题(有EDIT2和大量的评论)。有(或曾经有)许多答案清楚地表明了对这个问题的误解(我知道这也是我的错,对此表示抱歉) P>嗨,我已经看过了关于C++中虚拟继承的问题(类B:公共虚拟A{…} /Class >),但是没有找到我的问题的答案。 我知道虚拟继承有一些问题,但我想知道的是,在哪些情况下,虚拟继承会被认为是一个好的设计 我看到有人提到像IUnknown或ISerializable这样的接口,而且iostream设计基于虚拟继承。这些是虚拟继承的

EDIT3:请确保在回答之前清楚地理解我的问题(有EDIT2和大量的评论)。有(或曾经有)许多答案清楚地表明了对这个问题的误解(我知道这也是我的错,对此表示抱歉)

P>嗨,我已经看过了关于C++中虚拟继承的问题(<代码>类B:公共虚拟A{…} /Class >),但是没有找到我的问题的答案。 我知道虚拟继承有一些问题,但我想知道的是,在哪些情况下,虚拟继承会被认为是一个好的设计

我看到有人提到像
IUnknown
ISerializable
这样的接口,而且
iostream
设计基于虚拟继承。这些是虚拟继承的良好使用的好例子,是因为没有更好的选择,还是因为在这种情况下虚拟继承是正确的设计?谢谢

编辑:为了澄清,我问的是现实生活中的例子,请不要给出抽象的例子。我知道什么是虚拟继承,什么样的继承模式需要虚拟继承,我想知道的是,虚拟继承什么时候是做事情的好方法,而不仅仅是复杂继承的结果


EDIT2:换句话说,我想知道菱形层次结构(这是虚拟继承的原因)何时是一个好的设计

当您被迫使用多重继承时,需要虚拟继承。有一些问题无法通过避免多重继承来干净/轻松地解决。在这些情况下(这种情况很少见),您需要查看虚拟继承。95%的情况下,您可以(也应该)避免多重继承,以避免自己(以及那些在您之后查看代码的人)的许多麻烦


作为补充说明,COM不会强制您使用多重继承。创建从IUnknown(直接或间接)派生的具有线性继承树的COM对象是可能的(也是非常常见的)。

对于一个类a扩展另一个类B,但B除了可能的析构函数之外没有其他虚拟成员函数的情况,虚拟继承是一个很好的设计选择。您可以将像B这样的类视为,其中类型层次结构只需要一个mixin类型的基类就可以从中受益

一个很好的例子是虚拟继承,它与STL的libstdc++实现中的一些iostream模板一起使用。例如,libstdc++使用以下命令声明模板
basic\u istream

template<typename _CharT, typename _Traits>
class basic_istream : virtual public basic_ios<_CharT, _Traits>
模板
类基本流:虚拟公共基本流
它使用虚拟继承来扩展
basic_ios
,因为istream应该只有一个输入streambuf,并且一个istream的许多操作应该始终具有相同的功能(特别是
rdbuf
成员函数来获取一个且唯一的输入streambuf)


现在假设您编写了一个类(
baz_reader
),该类扩展了
std::istream
,使用一个成员函数读入
baz
类型的对象,另一个类(
bat_reader
)扩展了
std::istream
,使用一个成员函数读入
bat
类型的对象。您可以拥有一个扩展
baz_reader
bat_reader
的类。如果未使用虚拟继承,则
baz_读取器
bat_读取器
库将各自拥有自己的输入流,但这可能不是目的。您可能希望
baz_读取器
bat_读取器
base都从同一个streambuf读取。如果
std::istream
中没有虚拟继承来扩展
std::basic_ios
,您可以通过将
baz_读取器
bat_读取器
基的成员readbuf设置为相同的streambuf对象来实现这一点,但是,如果有一个就足够了,那么您将有两个指向streambuf的指针副本。

如果您有接口层次结构和相应的实现层次结构,则需要将接口基类设置为虚拟基

例如

通常,只有当您有许多扩展基本接口的接口以及不同情况下需要的多个实现策略时,这才有意义。这样,您就有了一个清晰的接口层次结构,并且您的实现层次结构可以使用继承来避免常见实现的重复。但是,如果您使用的是Visual Studio,则会收到很多警告C4250


为了防止意外切片,通常最好是
CBasicImpl
CExtendedImpl
类不可实例化,而是具有更高级别的继承,不提供额外的功能来保存构造函数。

Grrr。。虚拟继承必须用于抽象子类型。如果你要遵守OO的设计原则,那就别无选择。否则会阻止其他程序员派生其他子类型

首先是一个抽象示例:您有一些基本抽象A。您想创建一个子类型B。请注意,子类型必然意味着另一个抽象。如果它不是抽象的,那么它是一个实现而不是一个类型

现在又来了一个程序员,他想把a.Cool的子类型变成C

最后,另一个程序员来了,他想要的东西是B和C,当然也是a。在这些场景中,虚拟继承是必需的

下面是一个真实世界的示例:来自编译器,建模数据类型:

struct function { ..
struct int_to_float_type : virtual function { ..

struct cloneable : virtual function { .. 

struct cloneable_int_to_float_type : 
  virtual function, 
  virtual int_to_float_type 
  virtual cloneable 
{ ..

struct function_f : cloneable_int_to_float_type { 
这里,
function
表示函数,
int\u to\u float\u type
表示子类型 由int到float的函数组成<代码>可克隆性是一个特殊属性 该函数可以被克隆<代码>函数是一个具体的(非抽象的) 功能

请注意,如果我最初没有将
函数
设置为
int\u to\u float\u类型的虚拟基<
struct function { ..
struct int_to_float_type : virtual function { ..

struct cloneable : virtual function { .. 

struct cloneable_int_to_float_type : 
  virtual function, 
  virtual int_to_float_type 
  virtual cloneable 
{ ..

struct function_f : cloneable_int_to_float_type {