[C++模板技术] 线性继承体系自动生成

来源:互联网 发布:java中遍历数组 编辑:程序博客网 时间:2024/05/21 10:36

散乱的继承体系,或者说多重继承可能不是我们所需要的,尤其是当继承链中的基类中含有虚函数时,其可能会不可避免的造成为了维护虚函数表而产生的额外负担。除之外,很多我们的设计本身可能与多重继承不是很适配,因此,我们需要一个类似的,但是能够生成线性继承链的技法。

首先,假设我们有一个事件处理接口类:

template <typename T>class EventHandler{public:virtual void OnEvent(const T&, int EventID) = 0;virtual ~EventHandler() = default;};
我们可以利用前文提到的散乱继承体系生成器生成一个处理所有图形事件的接口:

using WidgetEventHandler = GenScatterHierarchy<TYPELIST_3(Window, Button, ScrollBar),EventHandler>;
但是需要注意的是,这样生成的接口类实际上是对于三个具有虚函数的基类的多重继承,针对每一个基类的部分,因为其含有虚函数,因此都需要维护一个虚函数表指针,在这里,一共就造成了3个虚表指针的额外负担,这显然不是我们想要的。为了解决这个问题,如果我们生成的继承体系是线性的。那么无论继承了多少个基类,我们都可以只用维护一个虚表指针了,这里,我们引入GenLinearHierarchy模板类:

template<typename TList,template <typename AtomicType, typename Base> class Unit,typename Root = EmptyType>class GenLinearHierarchy;
上述模板类是生成器的母版,为了能够完成线性递归继承,和散乱继承生成器不同,我们需要让模板类Unit额外提供一个基类参数,这个基类参数代表的是Unit模板类实例化后所继承的父类类型,在最终实例化后的继承体系中,每一个Unit实例的父类都将是一个GenLinearHierarchy实例,而最顶端的Unit实例的基类,也就是整个继承链的顶端基类,作为递归终点,我们利用Root参数来指定,默认的,Root类型为EmptyType,EmptyType具体构造与之前文章中的NullType实作方式相同,只是一个空结构体。

接着,我们提供一般的递归过程,偏特化这个类:

template<typename T1,typename T2,template <typename,typename> class Unit,typename Root>class GenLinearHierarchy<TypeList<T1,T2>,Unit,Root>:public Unit<T1,GenLinearHierarchy<T2,Unit,Root>>{};
接着,我们针对递归出口提供一个偏特化:

template<typename T,template <typename,typename> class Unit,typename Root>class GenLinearHierarchy<TypeList<T,NullType>, Unit, Root>:public Unit<T,Root>{};
以上就是全部内容了,我们可以比较一下两种生成器生成的接口的大小:

template <typename T>class EventHandler1{public:virtual void OnEvent(const T&, int EventID) = 0;virtual ~EventHandler1() = default;};template <typename T,typename Base>class EventHandler2:public Base{public:virtual void OnEvent(const T&, int EventID) = 0;virtual ~EventHandler2() = default;};using WidgetEventHandler1 = GenScatterHierarchy<TYPELIST_3(Window, Button, ScrollBar),EventHandler1>;using WidgetEventHandler2 = GenLinearHierarchy<TYPELIST_3(Window, Button, ScrollBar),EventHandler2>;int main(){// Test on VS2015cout << sizeof(WidgetEventHandler1) << endl; // 12 cout << sizeof(WidgetEventHandler2) << endl; // 4return 0;}
可以看出,采取多重继承的方式,我们需要维护3个虚表指针,sizeof后输出12,而采用线性继承的方式,我们只需要维护一个虚表指针,sizeof的结果是4。





原创粉丝点击