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


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM