我想在C ++中实现一个类层次结构:
简化我有这个代码:
#include <iostream>
class IClass {
public:
virtual int commonMethod() const = 0;
};
class Class : public virtual IClass {
protected:
int commonValue;
public:
Class(int commonValue) : commonValue(commonValue) {}
virtual int commonMethod() const {
return commonValue;
}
};
class IClassDerived : public virtual IClass {
public:
virtual void specialMethod() = 0;
};
class ClassDerived : public Class, public virtual IClassDerived {
public:
ClassDerived(int commonValue) : Class(commonValue) {}
virtual void specialMethod() {
// do something
}
};
class IClassDerived2 : public virtual IClassDerived {
public:
virtual void specialMethod2() = 0;
};
class ClassDerived2 : public ClassDerived, public virtual IClassDerived2 {
public:
ClassDerived2(int commonValue) : ClassDerived(commonValue) {}
virtual void specialMethod2() {
specialMethod();
}
};
class IClassDerived3 : public virtual IClassDerived2 {
public:
virtual int commonMethod() const override = 0;
};
class ClassDerived3 : public ClassDerived2, public virtual IClassDerived3 {
public:
ClassDerived3(int commonValue) : ClassDerived2(commonValue) {}
virtual int commonMethod() const override {
return 4711;
}
};
int main() {
ClassDerived foo(1);
ClassDerived2 foo2(2);
ClassDerived3 foo3(3);
std::cout << foo.commonMethod() << " " << foo2.commonMethod() << " " << foo3.commonMethod() << " " << std::endl;
// 1 2 4711
return 0;
}
我现在有两个问题:
答案 0 :(得分:4)
我最近发现了一种不需要虚拟继承的解决方法(见下文)。
基本上,在这种情况下,语言需要使用虚拟继承来直接解决问题,因为您从同一个类继承了多次。没有虚拟继承,你最终会得到这个:
Interface0 Interface0 Interface0 ^ ^ ^______ | | \ Interface1 Interface1 Impl0 ^ ^__________________ ^ | \ | Interface2 Impl1 ^______________________________ ^ \ | Impl2
有多个&#34;实例&#34; InterfaceX
基类的,它们是独立的。考虑路径Interface0
中的Impl1 -> Interface1 -> Interface0
实例。 Impl0
类不继承自 Interface0
的实例,因此它不实现其虚函数。请注意,如果所有这些接口类都是有状态类(使用数据成员)而不是纯接口,那么这很有用。
但是在这种特殊情况下,你只从接口继承,理论上理论上不需要虚拟继承。我们想看到以下图片:
Interface0 _ ^ |\ | \ Interface1 _ Impl0 ^ |\ ^ | \ | Interface2 _ Impl1 |\ ^ \ | Impl2
理论上,Impl1
可以定义一个单独的vtable,其中Impl0
的条目实现Interface0
的虚函数,Impl1
的函数实现Interface1
的虚函数{1}}。结果将是单个vtable,不需要偏移计算(因此不需要虚拟继承)。
但是,唉,语言并没有这样定义继承 - 它在抽象类和纯接口之间没有区别。如果通过虚拟继承继承它们,它允许您仅通过侧向继承(Impl0
覆盖Impl1 -> Interface1 -> Interface0
的虚函数)覆盖虚函数。通过虚拟继承,您指定确实只从Interface0
继承了一次,因此从Impl1
到Interface0
的两条路径(直接继承和Impl0
})yield 相同的类。
虚拟继承有几个缺点,因为它必须允许只能在运行时确定基类(相对于子对象)的位置的情况。 但是,有一种不需要虚拟继承的解决方法。
首先编写一个自包含的类,然后将其调整为接口通常更有用。接口通常由周围的体系结构定义,因此不一定是该类的通用。通过将接口与实现分离,您可以使用不同的接口重用实现。
如果我们把它放在一起:我们不能使用多重继承来实现虚函数的方法,我们希望将接口与实现分开。我们最终得出:要么,我们不要让我们的实现来自任何东西(这会导致样板代码),或者我们从线性推导出来,这是顶部接口的唯一继承。
通过将我们的实现类编写为类模板,我们可以线性派生并传递要从基础实现类派生的顶层接口:
struct Interface0 {
virtual void fun0() = 0;
};
struct Interface1 : Interface1 {
virtual void fun1() = 0;
};
struct Interface2 : Interface0 {
virtual void fun2() = 0;
};
template<typename Interface = Interface0>
struct Impl0 : Interface {
void fun0() {}
};
template<typename Interface = Interface1>
struct Impl1 : Impl0<Interface> {
void fun1() {}
};
template<typename Interface = Interface2>
struct Impl2 : Impl1<Interface> {
void fun2() {}
};
int main()
{
auto x = Impl2<Interface2>(); // or simply: Impl2<>()
Interface2* p = &x;
}
请注意,我们使用继承:在Impl1
方面实现Impl0
,并将扩展接口传递给我们的基类。
main
,Impl2<>
中的案例:
Interface0 ^ | Interface1 ^ | Interface2 _ |\ \ Impl0<Interface2> ^ | Impl1<Interface2> ^ | Impl2<Interface2>
另一个案例,Impl1<>
:
Interface0 ^ | Interface1 _ |\ \ Impl0<Interface1> ^ | Impl1<Interface1>
答案 1 :(得分:2)
您必须在所有派生类构造函数中初始化虚拟基类。因此,如果你有
class ClassDerived : public virtual Class, public virtual IClassDerived {
// ^^^^^^^
然后在ClassDerived2
和ClassDerived3
的构造函数中,您必须初始化Class
,尽管它似乎是在ClassDerived
的构造函数中初始化的:
ClassDerived2(int commonValue) : Class(commonValue), ClassDerived(commonValue) {}
ClassDerived3(int commonValue) : Class(commonValue), ClassDerived2(commonValue) {}
原因是[class.base.init]/7
:
mem-initializer ,其中 mem-initializer-id 表示虚拟基类在执行任何不是派生类最多的类的构造函数时被忽略
关于是否有更好的方法,我个人宁愿使用聚合而不使用虚拟继承,尽管它需要编写一些样板转发代码:
class IClass {
public:
virtual int commonMethod() const = 0;
};
class Class : public IClass {
protected:
int commonValue;
public:
Class(int commonValue) : commonValue(commonValue) {}
virtual int commonMethod() const {
return commonValue;
}
};
class IClassDerived : public IClass {
public:
virtual void specialMethod() = 0;
};
class ClassDerived : public IClassDerived { // no inheritance from Class
public:
ClassDerived(int commonValue) : m_class(commonValue) {}
virtual int commonMethod() const {
return m_class.commonMethod();
}
virtual void specialMethod() {
// do something
}
private:
Class m_class;
};
// and so on