C ++中的非对称虚拟继承菱形

Posted

技术标签:

【中文标题】C ++中的非对称虚拟继承菱形【英文标题】:Asymmetric virtual Inheritance diamond in C++ 【发布时间】:2009-08-07 15:34:24 【问题描述】:

所以我有这个想法,我认为用 C++ 实现它基本上是不可能的......但我想问一下。我通读了 Stroustrup 的第 15 章并没有得到答案,而且我认为关于继承钻石的其他十亿个问题也无法回答这个问题,所以我在这里问。

问题是,当您从两个本身共享一个公共基类的基类继承,但两个基类中只有一个虚拟继承自它时,会发生什么情况。例如:

class CommonBase  ... ;

class BaseA : CommonBase  ... ;

class BaseB : virtual CommonBase  ... ;

class Derived : BaseA, BaseB  ... ;

我想这样做的原因是因为我正在尝试扩展现有库而不必重新编译整个库(不想打开那罐蠕虫)。已经存在我想要修改的继承链。基本上是这样的(请原谅 ascii 艺术)

    LibBase
         | \
         |  \ 
         |   MyBase
         |     |
         |     |
 LibDerived    |
         | \   |
         |  \  |
         |   MyDerived
         |     |
LibDerived2    |
         | \   |
         |  \  |
         |   MyDerived2
         |     |
LibDerived3    |
         | \   |
         |  \  |
         |   MyDerived3
         |     |
LibConcrete    |
           \   |
            MyConcrete

得到图片?我希望每个“My”类的对象成为它们实质上要替换的类的对象,但我希望继承图中的下一个类使用“ My" 基类,但库类中的所有其他方法。库类不虚拟继承所以是这样的

class LibDerived : LibBase

但是如果我让我的类虚拟继承

class MyBase : virtual LibBase ;
class MyDerived: virtual MyBase, virtual LibDerived ;

既然MyDerived 将有一个vtable,MyBase 将有一个vtable,那么LibBase 是否只有一个对象?

我希望这个问题足够清楚。

【问题讨论】:

你到底为什么要这样做? :p 正如我所说,我想通过创建一个覆盖库中类的成员函数的类来扩展库的功能,但是从该类继承的对象仍然可以供图书馆使用。我要扩展的具体类与我要更改其实现的基类有 4 级继承。我可以看到我可能需要在其他派生类中重用该功能,因此仅在我的具体类中覆盖必要的方法是不够的。 【参考方案1】:

为了简化答案,让我们将虚拟/非虚拟视为重复或非重复的内容。

class LibDerived : LibBase

声明:我允许 LibBase 两次(或更多)进入 LibDerived 的降序

class MyBase : virtual LibBase ;

声明:我允许编译器将 MyBase 降序中的两个 LibBase 条目优化为单个条目。

当这两个声明同时满足时,第一个具有更高的优先级,因此 MyDerived 获得了 LibBase 的 2 个实现。但是 c++ 的力量是解决它的可能性!只需覆盖 MyDerived 虚函数以选择要使用的虚函数。或者另一种方式 - 创建从接口 LibBase 派生的 MyDerived 的通用包装器,该接口聚合任何实例:LibDerived、MyBase、...并从聚合中调用预期的方法。

【讨论】:

你能进一步解释你提到的分辨率吗?当您说重写 MyDerived 虚函数以选择我想要的虚函数时,您的意思是“接受您有两个 LibBase 对象的事实,并重写方法以仅公开其中一个”吗?不幸的是,我认为通用包装器不会做我想做的事情,因为它会与库的其余部分不兼容。我希望每个派生类对象“出现”就好像它是库的本机对象,但只是对某些方法有不同的实现。 virtual 在继承中不允许编译器优化基类。 virtual 基类用于保证无论该类型作为虚拟基类在派生类对象的继承树中出现多少次,都只有一个基类子对象。保证非虚拟基类添加新的基类子对象。没有优先级,也没有为同一类型添加第二个虚拟基地的选项。 Cheshirekow,让我按顺序回答。聚合完全允许在多个实现之间进行选择,您只需围绕 impl 进行包装。并且 MyConcrete,例如在构造函数时,接受指向预期实现的指针,并通过将每个调用延迟到 agreate 来公开它。 “接受你有两个 LibBase 的事实” - 不幸的是,“是”非虚拟声明授予您手动覆盖每个虚拟函数以决定必须调用两个副本的哪个成员。【参考方案2】:

基本上,你是对的。如果你想让这种继承树工作,你需要让LibDerived虚拟派生自LibBase

如果你没有这个,你不能阻止在LibDerived 下有一个非虚拟LibBase,在MyBase 下有一个单独的虚拟LibBase

【讨论】:

所以层次结构中的每个非虚拟继承链接都会引入一个新的基础对象,并且每个虚拟继承链接都会被压缩成一个单独的其他基础对象? 没错。 all 有一个虚拟基类实例,当一个基类在层次结构中显示为虚拟基时 plus each 有一个基类实例类在层次结构中显示为非虚拟基类的时间。

以上是关于C ++中的非对称虚拟继承菱形的主要内容,如果未能解决你的问题,请参考以下文章

C++进阶第十五篇—C++中的继承(继承的概念+菱形继承+虚拟继承+组合)

C++之继承总结(继承的定义与格式,赋值转换,默认成员函数,菱形继承及菱形虚拟继承)

C++-继承

C++继承详解

C++继承详解

C++进阶:继承C++为什么要引入继承 | 继承概念及定义 | 基类和派生类对象赋值转换 | 继承中的作用域 | 派生类的默认成员函数 | 继承与友元/静态成员 | 复杂的菱形继承及菱形虚拟继承