Step By Step(C++模板和继承)
一、命名模板参数:
有些高级脚本语言,如Perl、PL/SQL等,他们的函数参数在调用时都支持命名参数,既在调用时可以不按照顺序传递参数,而是p可以按照参数的名字传递。先看下面的代码示例:
template<typename Policy1 = DefaultPolicy1,
typename Policy2 = DefaultPolicy2,
typename Policy3 = DefaultPolicy3,
typename Policy4 = DefaultPolicy4>
class BreadSlicer {
... ...
}
上面的模板类含有4个模板参数,如果要想指定其中的某个参数不为缺省参数,那么也必须同时指定其之前的所有模板参数,如:
BreadSlicer<DefaultPolicy1,DefaultPolicy2,Custom>,然而我们更希望使用这样的调用形式:BreadSlicer<Policy3 = Custom>。下面将给出一些具体的实现,请务必留意代码中的关键性注释:
1 #include <stdio.h> 2 #include <typeinfo> 3 #include <conio.h> 4 5 //先定义出不同的策略类。 6 class DefaultPolicy1 {}; 7 class DefaultPolicy2 {}; 8 class DefaultPolicy3 {}; 9 class DefaultPolicy4 {}; 10 11 //该类将会是所有Policy Class的基类。他提供了缺省的四个Policy的类型重定义。 12 //因此在缺省情况下,这四个Policy将会是BreadSlicer的四个Policy。 13 class DefaultPolicies { 14 public: 15 typedef DefaultPolicy1 P1; 16 typedef DefaultPolicy2 P2; 17 typedef DefaultPolicy3 P3; 18 typedef DefaultPolicy4 P4; 19 }; 20 21 //这里之所以给出中间类DefaultPolicyArgs,同时又让该类以虚拟继承的方式继承 22 //DefaultPolicies,一是为了避免后面在多重继承同一基类时而导致的二义性,同时 23 //也是为了方便后面其他类的继承。 24 class DefaultPolicyArgs : virtual public DefaultPolicies { 25 }; 26 27 //这里之所以有第二个常量模板参数,是为了避免重复继承相同的基类。 28 template<typename Base, int D> 29 class Discriminator : public Base { 30 }; 31 32 //在这里,如果没有Discriminator的常量模板参数,将极有可能导致继承同一个基类。 33 template<typename Setter1, typename Setter2, 34 typename Setter3, typename Setter4> 35 class PolicySelector : public Discriminator<Setter1,1>,public Discriminator<Setter2,2>, 36 public Discriminator<Setter3,3>, public Discriminator<Setter4,4> { 37 }; 38 39 template<typename PolicySetter1 = DefaultPolicyArgs, 40 typename PolicySetter2 = DefaultPolicyArgs, 41 typename PolicySetter3 = DefaultPolicyArgs, 42 typename PolicySetter4 = DefaultPolicyArgs> 43 class BreadSlicer { 44 public: 45 //在该类后面的实现中,不要直接使用模板参数,而是要使用Policies::P1, P2, P3, P4等。 46 typedef PolicySelector<PolicySetter1,PolicySetter2,PolicySetter3,PolicySetter4> Policies; 47 void DoTest() { 48 printf("Policies::P1 is %s\n",typeid(Policies::P1).name()); 49 printf("Policies::P2 is %s\n",typeid(Policies::P2).name()); 50 printf("Policies::P3 is %s\n",typeid(Policies::P3).name()); 51 printf("Policies::P4 is %s\n",typeid(Policies::P4).name()); 52 } 53 }; 54 55 template<typename Policy> 56 class Policy1_is : virtual public DefaultPolicies { 57 public: 58 typedef Policy P1; //改写DefaultPolicies中的基于P1的typedef。 59 }; 60 61 template<typename Policy> 62 class Policy2_is : virtual public DefaultPolicies { 63 public: 64 typedef Policy P2; //改写DefaultPolicies中的基于P2的typedef。 65 }; 66 67 template<typename Policy> 68 class Policy3_is : virtual public DefaultPolicies { 69 public: 70 typedef Policy P3; //改写DefaultPolicies中的基于P3的typedef。 71 }; 72 73 template<typename Policy> 74 class Policy4_is : virtual public DefaultPolicies { 75 public: 76 typedef Policy P4; //改写DefaultPolicies中的基于P4的typedef。 77 }; 78 79 class CustomPolicy {}; 80 81 int main() { 82 BreadSlicer<Policy3_is<CustomPolicy> > bc; 83 bc.DoTest(); 84 getch(); 85 return 0; 86 } 87 //Policies::P1 is class DefaultPolicy1 88 //Policies::P2 is class DefaultPolicy2 89 //Policies::P3 is class CustomPolicy 90 //Policies::P4 is class DefaultPolicy4
在上面的例子中一个非常重要的特点是,所有的模板实参都是DefaultPolicies的派生类。在声明BreadSlicer对象时,不同的派生类将覆盖不同的DefaultPolicies中的typedef。
二、递归模板模式:
这是一种通用的模板设计模式,即派生类将本身作为模板参数传递给基类,如:
template<typename DerivedT>
class Base {
... ...
};
class MyDerived : public Base<MyDerived> {
... ...
};
基于这种模式,有一个非常著名的用例,即[MeyersCounting],是《Effective C++》的作者Scott Meyers所设计的。通过继承以下代码中的基类,所有的派生类便可实现类实例计数的功能。在下面的基类中,将包含一个表示对象计数的静态成员,同时还会在基类构造的时候递增该值,并在析构的时候递减该值,见如下代码示例:
1 #include <stdio.h> 2 #include <conio.h> 3 4 template<typename CountedType> 5 class ObjectCounter { 6 private: 7 static size_t count; 8 9 protected: 10 ObjectCounter() { 11 ++ObjectCounter<CountedType>::count; 12 } 13 ObjectCounter(ObjectCounter<CountedType> const&) { 14 ++ObjectCounter<CountedType>::count; 15 } 16 ~ObjectCounter() { 17 --ObjectCounter<CountedType>::count; 18 } 19 20 public: 21 static size_t liveCount() { 22 return ObjectCounter<CountedType>::count; 23 } 24 }; 25 26 template<typename CountedType> 27 size_t ObjectCounter<CountedType>::count = 0; 28 29 //C++编译器会根据模板参数的不同实例化不同类型的类对象,因此模板参数不同,所使用的静态成员也是不同的。 30 class MyClass : public ObjectCounter<MyClass> { 31 }; 32 33 int main() { 34 MyClass mc1; 35 printf("The count of MyClass is %d\n",MyClass::liveCount()); 36 { 37 MyClass mc2; 38 printf("The count of MyClass is %d\n",MyClass::liveCount()); 39 } 40 printf("The count of MyClass is %d\n",MyClass::liveCount()); 41 getch(); 42 return 0; 43 } 44 //The count of MyClass is 1 45 //The count of MyClass is 2 46 //The count of MyClass is 1