为类的子集分解纯虚函数的实现

时间:2011-08-12 17:13:20

标签: c++ multiple-inheritance

假设我有这个类层次结构:

class A {
public:
    virtual void foo(Base *b) = 0;
};

class B {
public:
    virtual void bar() = 0;
};


class C : public A, public B {
public:
    virtual void viz() = 0;
};

class E : public C {
public:
    /** Some non-pure virtual functions, does not implement foo */
};

class F : public E {
    /** does not implement foo */
}

class G : public E {
    /** does not implement foo */
}

现在,我想定义F和G的派生的,“模板化的”版本,它声明了一个方法foo(T *s)foo(Base *s)的实现通过应用简单地调用foo(T * s)一个static_cast。

此外,我不想将模板参数暴露给A,因为这些接口(A,B,C,E)的用户不应该暴露给模板参数(我正在编写插件架构和用户)派生给定接口的-types从插件传递到基本系统。)

我认为定义这个类:

template <typename T>
class GA : public A{
...
}

并定义F和G的“模板化”推导,如下所示:

template <typename T>
class GF : public F, public GA {
...
}

问题是GF看到两个不同的foo()声明(一个由A给出,另一个由GA给出),所以当我尝试实例化GF时,编译器会抛出一个错误,因为没有定义foo(Base)。

我该如何解决这个问题?

5 个答案:

答案 0 :(得分:4)

为什么需要多重继承?为什么不只是class C : public Impl { ... }

答案 1 :(得分:2)

我认为问题是Impl继承了课程A而课程C继承了ImplA。这意味着将有两个类A的实例,其中第一个A的fooImpl实现,而秒一个仍然是纯虚拟的。虚拟继承可以解决这个问题,例如:

class A {
public:
    virtual void foo () = 0;
};

class Impl : virtual public A {
public:
    virtual void foo () {}
};

class C
    : virtual public A
    , public Impl
{
};

答案 2 :(得分:2)

我会建议一些不同的东西。将该函数声明为纯虚函数,然后通过转发调用在衍生类中简单地实现它:

class A {
public:
  virtual void foo() = 0;
};
void A::foo() {}

class B : public A {
   void foo() { A::foo(); }
};

但这并不意味着你无法实现你想要做的事情。如果要在另一个类中实现该操作并使用继承,可以通过两种不同的方式实现:

中级类型:

实现提供功能的中间类型,然后每个派生类型可以扩展原始A或中间I类型:

class I : public A {
public:
   void foo() {}
};
class B : public I {};      // inherits the implementation
class C : public A {        // needs to provide it's own implementation
   void foo() {}
};

虚拟继承:

或者你可以使用虚拟继承(我会避免这三种选择)。通过这种方式,您可以创建一个层次结构,其中V实际上从A继承并提供实现。其余类可以直接从A继承,也可以从A虚拟继承,也可以从V继承。通过使用虚拟继承,您可以确保层次结构中有一个A子对象:

class V : public virtual A {
public:
   void foo() {}
};
class B : public virtual A, V { // Inheritance from V is a detail, can be private
                                // no need to provide foo
};

请注意,这种方法对于这个特殊的有限问题来说是过度的,并且它有副作用(对象的布局会有所不同,而且它们的大小会略大)。同样,除非你想混合和匹配来自不同兄弟类的函数,否则我会避免这种情况。

您的代码出了什么问题

您的代码中的问题是您从A多次继承,一次直到D,另一次直接继承。在D的路径中,虚函数不再是纯粹的,在A的直接路径中,虚函数仍未定义。

这也导致错误消息中的第二个问题:歧义。因为有两个A子对象,当您尝试在最派生类型上调用foo时,编译器无法确定要执行请求的两个A子对象中的哪一个。如果您尝试直接从最派生类型转换为A,则会出现相同类型的歧义问题,因为编译器不知道您要引用哪个A

答案 3 :(得分:1)

Impl没有在D和C中实现A的方法,它是一个完全不同的基础。

您应该从A派生Impl,然后从Impl而不是A派生该子集的每个类。

答案 4 :(得分:0)

如果要继承A和Impl,则需要使用虚拟继承。

编辑:

有关详细信息,请参阅@ Vlad的答案。