2008-10-31 59 views
22

我有一个抽象基类,它充当接口。来自两个派生类的多继承

我有两套派生类,它们实现了抽象类的一半。 (一个“set”定义了与初始化有关的抽象虚拟方法,另一个“set”定义了与实际“work”相关的那些)。然后我有派生类,它们使用多重继承来构造完全定义的类本身不添加任何东西)。

所以:(坏的伪代码)

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

似乎虚拟继承是这里的解决方案。我以前听说过虚拟继承,但是我没有把头绕在它周围。我仍然乐于接受建议。

回答

33

它看起来像你想做虚拟继承。这是否证明实际上是一个好主意是另一个问题,但这里是你怎么做:


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

基本上,默认情况下,非虚多重继承将包括副本的每个基类在派生类,并包括他们所有的方法。这就是为什么你有两个AbsBase副本 - 并且你的方法使用的原因是模糊的,因为两组方法都被加载,所以C++无法知道要访问哪个副本!

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

1

它可以做到,尽管它给人最多的不安。

你需要使用“虚拟继承”,其语法是像

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

然后,你必须指定要使用的功能:

NotAbsTotal::work() 
{ 
    AbsInit::work_impl(); 
} 

(更新了正确的语法)

1

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

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; 
+0

这个结果不错,即使我非虚公共方法。但是,我将不得不尝试看看在将基类型指针用于派生类型的对象时会发生什么情况,以确保虚拟继承像虚函数重载一样工作:) – mmocny 2008-10-31 20:21:38

+0

当从虚拟基类转换为派生类时,总是需要使用dynamic_cast <>,这又要求在虚拟基础中定义虚拟方法。虚拟基地的简单使用应该没有问题 - 它将以多态方式授予对派生类的访问权限。 – 2008-10-31 23:29:10

0

你必须开始思考你在这里试图建模的东西。

公共继承应该只能用来模拟“isa”关系,例如,狗是一种动物,方形是一种形状等。

看看斯科特迈尔的书“有效的C++”一本关于面向对象设计的各个方面应该被解释为什么的优秀论文。

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

HTH

欢呼声,

罗布