模板与继承

it2024-11-22  20

@[TOC]模板与继承

命名模板参数

命名模板参数主要利用了C++继承中的一个规则,子类重定义,会隐藏基类中定义:

class DefaultPolicy1 { public: void print() { cout << "Policy 1" << endl; } }; class DefaultPolicy2 { public: void print() { cout << "Policy 2" << endl; } }; class DefaultPolicies { public: typedef DefaultPolicy1 P1; typedef DefaultPolicy2 P2; }; class DefaultPolicyArgs : virtual public DefaultPolicies { }; class Policy1_is : virtual public DefaultPolicies { public: typedef DefaultPolicy1 P2; }; class Policy2_is : virtual public DefaultPolicies { public: typedef DefaultPolicy2 P1; }; class PolicySelector : public DefaultPolicyArgs, public Policy2_is { }; int main() { PolicySelector::P1 p; p.print(); }

上面例子中,Policy2_is重定义了P1,所以在PolicySelector中P1是DefaultPolicy2,结果输出:Policy 2。

DefaultPolicyArgs 类似乎是毫无意义的。如果删除DefaultPolicyArgs定义,PolicySelector定义改为:

class PolicySelector : public DefaultPolicies, public Policy2_is { };

编译器会提示PolicySelector::P1不明确。

在上面例子中加入模板:

class DefaultPolicy1 { }; class DefaultPolicy2 { }; class DefaultPolicy3 { }; class DefaultPolicy4 { }; class SpecialPolicy { }; class DefaultPolicies { public: typedef DefaultPolicy1 P1; typedef DefaultPolicy2 P2; typedef DefaultPolicy3 P4; typedef DefaultPolicy3 P4; }; class DefaultPolicyArgs : virtual public DefaultPolicies { }; template <typename Policy> class Policy_is1 : virtual public DefaultPolicies { public: typedef Policy P1; }; template <typename Policy> class Policy_is2 : virtual public DefaultPolicies { public: typedef Policy P2; }; template <typename Policy> class Policy_is3 : virtual public DefaultPolicies { public: typedef Policy P3; }; template <typename Policy> class Policy_is4 : virtual public DefaultPolicies { public: typedef Policy P4; }; typename <typename Base, int D> class Discriminator : public Base { }; template <typename Setter1, typename Setter2, typename Setter3, typename Setter4> class PolicySelector : public Discriminator<Setter1, 1>, public Discriminator<Setter2, 2>, public Discriminator<Setter3, 3>, public Discriminator<Setter4, 4> { }; template <typename PolicySetter1 = DefaultPolicyArgs, typename PolicySetter2 = DefaultPolicyArgs, typename PolicySetter3 = DefaultPolicyArgs, typename PolicySetter4 = DefaultPolicyArgs> class BreadSlicer { public: typedef PolicySelector<PolicySetter1, PolicySetter2, PolicySetter3, PolicySetter4> Policies; };

现在,使用BreadSlicer可以直接指定任何一个模板实参。如果定义一个BreadSlicer<Policy3_is >变量,那么PolicySelector相当于继承了DefaultPolicyArgs和Policy3_is,所以P3被重定义为SpecialPolicy类。模板Discriminator的引用是为了避免继承同一个类。

空基类优化EBCO

class EmptyClass { };

一般地,定义了一个EmptyClass这样的空类,其对象的大小也不会为0,也就是说需要分配内存。 但是C++规定,当空类作为基类时,只要不会与同一类型的另一个对象或者子对象分配在同一地址,就不需要为其分配内存。这就是空基类优化。

class Empty { }; class EmptyToo : public Empty { }; class EmptyThree : public EmptyToo { };

在支持空基类优化的编译器环境中,上面代码中的3个类大小相同,但都不为0。也就是说EmptyToo和EmptyThree的基类都不占内存。C++环境中不存在真正意义上不占内存的类,空基类优化是针对空类作为基类时,在子类中是否需要分配内存的一个规则。

class NonEmpty : public Empty, public EmptyToo { };

NonEmpty类中的基类Empty和EmptyToo都是占内存。否则,NonEmpty中的Empty直接基类和EmptyToo中的Empty间接基类地址就会重合,这违反了空基类优化规则。

下面是空基类优化的一个应用例子(更多情况可百度boost.compressed_pair):

template <typename CustomClass> class Optimizable { private: CustomClass info; void* storage; };

其中CustomClass一定是类类型,且可能为空,那么可以优化:

template <typename CustomClass> class Optimizable { private: BaseMemberPair<CustomClass, void*> info_and_storage; }; template <typename Base, typename Member> class BaseMemberPair : private Base { private: Member member; public: BaseMemberPair(Base const &b, Member const &m) : Base(b), member(m) {} Base const& first() const { return (Base const&)*this; } Base& first() { return (Base&)*this; } Member const& second() const { return this->member; } Member& second() { return this=>member; } };

奇特的递归模板模式CRTP

CRTP的主要特点是派生类将自身作为模板参数传给基类。

下面例子是一个数某个类对象个数的类模板。当需要计算某个类对象个数时,只要继承ObjectCount模板就可以,模板实参自然是类本身:

template <typename CountedType> class ObjectCounter { private: static size_t count; protected: ObjectCounter() { ++ObjectCounter<CountedType>::count; } ObjectCounter(ObjectCounter<CountedType> const&) { ++ObjectCounter<CountedType>::count; } ~ObjectCounter() { --ObjectCounter<CountedType>::count; } public: static size_t live() { return ObjectCounter<CountedType>::count; } }; template <typename CountedType> size ObjectCounter<CountedType>::count = 0;

CRTP把类的功能提取出来,放到一个依赖于自身的模板类中实现,再继承模板类重新获取该功能。如果只有一个类需要,或者只有一个类通过该模式能够实现功能,那该模式是没有意义。CRTP的本质是为了泛化,所以为了不失一般性,一般只提取仅能用作成员函数的接口(构造函数、析构函数、下标运算符Operator[]等)的功能。换句话说,如果有一个功能是在仅能用作成员函数的接口中实现的,并且意图把功能应用到其他类时,CRTP是一个不错的选择。

最新回复(0)