我想在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;
}
Run Code Online (Sandbox Code Playgroud)
我现在有两个问题:
我最近发现了一种不需要虚拟继承的解决方法(请参见下文)。
基本上,在这种情况下,这种语言需要使用虚拟继承直接解决问题,因为您从同一个类继承了多次。没有虚拟继承,您将得到以下结果:
接口0接口0接口0
^ ^ ^ ______
| | \
接口1接口1 Impl0
^ ^ __________________ ^
| \ |
接口2 Impl1
^ ______________________________ ^
\ |
Impl2
InterfaceX基类有多个独立的“实例” 。考虑Interface0路径中的实例Impl1 -> Interface1 -> Interface0。本Impl0类不继承该实例Interface0,因此它并没有实现其虚函数。请注意,如果所有这些接口类都是有状态类(具有数据成员),而不是纯接口,则这很有用。
但是在这种特殊情况下,您仅从接口继承,理论上就不需要虚拟继承。我们想看下图:
接口0 _
^ | \
| \
接口1 _ Impl0
^ | \ ^
| \ |
接口2 _ Impl1
| \ ^
\ |
Impl2
从理论上讲,Impl1可以定义与来自条目单个虚表Impl0从执行虚拟函数Interface0,并从功能上Impl1从执行虚拟功能Interface1。结果将是单个vtable,不需要进行偏移量计算(因此不需要虚拟继承)。
但是,可惜,这种语言没有以这种方式定义继承-它在抽象类和纯接口之间没有区别。如果您通过虚拟继承继承Impl0虚拟函数,Impl1 -> Interface1 -> Interface0则它仅允许您通过侧向继承来覆盖虚拟函数(覆盖的虚拟函数)。在几乎继承,你指定你确实只继承一次从Interface0,所以从两个路径Impl1到Interface0(直接继承和通过Impl0)产生相同的类。
虚拟继承有几个缺点,因为它必须允许仅在运行时才能确定基类(相对于子对象)的位置的情况。但是,有一种解决方法不需要虚拟继承。
首先编写一个自包含的类,然后将其改编成接口通常会更有用。该接口通常由周围的体系结构定义,因此不一定是该类的通用对象。通过将接口与实现分开,您可以使用其他接口重用实现。
如果我们将它们放在一起:我们不能使用多重继承来横向实现虚拟函数,而我们想要将接口与实现分开。我们最终得出:或者,要么不让我们的实现派生任何东西(这导致样板代码),要么我们线性地派生这是从顶部接口获得的唯一继承。
通过将实现类编写为类模板,我们可以进行线性派生并将顶部的派生接口传递给基本实现类:
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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,我们将继承用于以下两个方面:就实现Impl1而言Impl0,以及将扩展接口传递给我们的基类。
在该情况下main,Impl2<>:
接口0
^
|
接口1
^
|
接口2 _
| \
\
Impl0 <接口2>
^
|
Impl1 <接口2>
^
|
Impl2 <接口2>
另一种情况Impl1<>:
接口0
^
|
接口1 _
| \
\
Impl0 <接口1>
^
|
Impl1 <接口1>