假设我有这个类层次结构:
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)。
我该如何解决这个问题?
答案 0 :(得分:4)
为什么需要多重继承?为什么不只是class C : public Impl { ... }
?
答案 1 :(得分:2)
我认为问题是Impl
继承了课程A
而课程C
继承了Impl
和A
。这意味着将有两个类A
的实例,其中第一个A的foo
由Impl
实现,而秒一个仍然是纯虚拟的。虚拟继承可以解决这个问题,例如:
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的答案。