来自两个派生类的多重继承

Posted

技术标签:

【中文标题】来自两个派生类的多重继承【英文标题】:Multiple Inheritance from two derived classes 【发布时间】:2010-09-20 06:14:00 【问题描述】:

我有一个作为接口的抽象基类。

我有两个“集”派生类,它们实现了抽象类的一半。 (一个“set”定义了与初始化相关的抽象虚方法,另一个“set”定义了与实际“工作”相关的那些。)

然后我有派生类,它们使用多重继承来构造完全定义的类(并且本身不添加任何东西)。

所以:(错误的伪代码)

class AbsBase 
  virtual void init() = 0;
  virtual void work() = 0;


class AbsInit : public AbsBase 
  void init()  do_this(); 
  // work() still abs


class AbsWork : public AbsBase 
  void work()  do_this(); 
  // init() still abs


class NotAbsTotal : public AbsInit, public AbsWork 
  // Nothing, both should be defined

首先,我可以这样做吗?我可以从两个派生自同一个 Base 的类继承吗? (希望如此)。

不过,这是“真正的问题”(我在上面撒了一点谎以简化示例)。

我真正做的是将非抽象访问器方法添加到基类:

class AbsBase 
public:
  void init()  init_impl(); 
  void work()  work_impl(); 

private:
  virtual void init_impl() = 0;
  virtual void work_impl() = 0;

因为,一个常见的习惯是让所有的虚拟方法都是私有的。

不幸的是,现在 AbsInit 和 AbsWork 都继承了这些方法,因此 NotAbsTotal 继承了“每个中的两个”(我意识到我可能正在扼杀编译时真正发生的事情)。

无论如何,g++ 在尝试使用该类时抱怨:“对成员 init() 的请求不明确”。

我假设,如果我将 AbsBase 类用作纯接口,则可以避免这种情况(假设上面的示例有效)。

所以: - 我的实施是否遥遥无期? - 这是将虚拟方法设为私有的习语的限制吗? - 我如何重构我的代码来做我想做的事? (提供一个通用接口,但允许一种方法将实现替换为“集合”成员函数)

编辑:

看来我不是第一个: http://en.wikipedia.org/wiki/Diamond_problem

似乎虚拟继承是这里的解决方案。我以前听说过虚拟继承,但我还没有深入了解它。我仍然愿意接受建议。

【问题讨论】:

【参考方案1】:

这是可以做到的,尽管它让大多数人不寒而栗。

您需要使用“虚拟继承”,其语法类似于

class AbsInit: public virtual AbsBase ...;
class AbsWork: public virtual AbsBase ...;
class NotAbsTotal: public AbsInit, public AbsWork ...;

然后你必须指定你想使用哪个函数:

NotAbsTotal::work()

    AbsInit::work_impl();

(已更新,语法正确)

【讨论】:

【参考方案2】:

您需要将继承声明为虚拟:

struct AbsBase 
          virtual void init() = 0;
          virtual void work() = 0;
;

struct AbsInit : virtual public AbsBase 
          void init()   
;

struct AbsWork : virtual public AbsBase 
          void work()  
;

struct NotAbsTotal : virtual public AbsInit, virtual public AbsWork 
;

void f(NotAbsTotal *p)

        p->init();


NotAbsTotal x;

【讨论】:

这很好,即使使用我的非虚拟公共方法。但是,我将不得不尝试看看在使用 Base 类型的指针指向 Derived 类型的对象时会发生什么,以确保虚拟继承像虚拟函数重载一样工作:) 从虚拟基类转换为派生类时,总是需要使用dynamic_cast,这又需要在虚拟基类中定义一个虚拟方法。简单地使用虚拟基类应该没有问题——它将多态地提供对派生类的访问。【参考方案3】:

看起来你想做虚拟继承。这是否真的是一个好主意是另一个问题,但这是你如何做到的:


class AbsBase ...;
class AbsInit: public virtual AbsBase ...;
class AbsWork: public virtual AbsBase ...;
class NotAbsTotal: public AbsInit, public AbsWork ...;

基本上,默认的非虚拟多重继承将在派生类中包含每个基类的副本,并包含它们的所有方法。这就是为什么您有两个 AbsBase 副本的原因——您的方法使用不明确的原因是两组方法都已加载,因此 C++ 无法知道要访问哪个副本!

虚拟继承将所有对虚拟基类的引用压缩到一个数据结构中。这应该使基类中的方法再次明确。但是,请注意:如果两个中间类中有额外的数据,可能会有一些额外的运行时开销,以使代码能够找到共享的虚拟基类。

【讨论】:

你为什么这么说?除了语法复杂性之外,虚拟继承还有哪些含义? 基指针到派生类的转换通常涉及指针调整,使用动态查找表。这种额外的间接引入了从基础转换为派生的成本。调用虚方法时,这种开销就在调用的噪音中。【参考方案4】:

您必须开始考虑您要在此处建模的内容。

公共继承只能用于建模“isa”关系,例如狗是动物,正方形是形状,等等。

请参阅 Scott Meyer 的《Effective C++》一书,了解一篇关于 OO 设计的各个方面只应被解释为的优秀文章。

编辑:我忘了说,虽然到目前为止提供的答案在技术上是正确的,但我认为它们中的任何一个都不能解决你试图建模的问题,这就是你问题的症结所在!

HTH

干杯,

罗伯

【讨论】:

【参考方案5】:

我在以下链接中找到了一个很好且简单的示例。本文通过一个计算矩形面积和周长的示例程序进行了说明。你可以检查一下..干杯

多级继承是一种继承层次结构,其中一个派生类从多个基类继承。阅读更多..

http://www.mobihackman.in/2013/09/multiple-inheritance-example.html

【讨论】:

以上是关于来自两个派生类的多重继承的主要内容,如果未能解决你的问题,请参考以下文章

大型程序的工具——多重继承与虚继承

如果 QObject 是从 DIRECTLY 派生的,那么使用 *virtual* 多重继承是不是安全?

来自两个抽象类的多重继承 (Qt)

C++ 多重继承

Python类的多重继承问题深入分析

多态虚函数表底层实现多重继承的问题及处理