zoukankan      html  css  js  c++  java
  • Step By Step(C++模板类)

        和函数一样,C++中的class也可以类型参数化,其中容器类是极具这一特征的。对于模板类的基本定义和使用,可以参考STL,这里就不做过多的赘述了。下面将主要介绍一下与其相关的高级实用特征。

    一、模板的特化:

        这里可以先将类模板特化与面向对象中的多态进行一个简单的比较,这样可以便于我们对它的理解,也同样有助于指导我们在实际的开发中应用这一C++技巧。众所周知,对于多态而言,提供的是统一的接口和不同的实现类实例,其最终的行为将取决于实现类中的实现,相信每一个有面向对象基础的开发者对于这一概念并不陌生。而模板特化则要求必须提供一个标准的模板类(等同于多态中的接口),与此同时再为不同的类型提供不同的特化版本。在模板特化类中,我们首先需要保证类名是相同的,只是模板参数不再使用抽象的类型,而是直接使用具体的类型来实例化该模板类。在调用时,编译器会根据调用时的类型参数自动选择最为合适的模板类,即如果有特化模板类中的类型参数和当前调用类型相匹配的话,则首选该特化模板类,否则选择最初的标准模板类。见如下用例(请注意代码中的说明性注释)

      1     #include <stdio.h>
      2     #include <string.h>
      3     
      4     //1. 这里我们先声明了一个通用类型的模板类。这里要有类型参数必须包含hashCode()方法。
      5     //否则,该类型在编译期实例化时将会导致编译失败。
      6     template <typename T>
      7     class CalcHashClass { //该类为标准模板类(等同于多态中的接口)
      8     public:
      9         CalcHashClass(T const& v) : _value(v) {
     10         }
     11         int hashCode() {
     12             printf("This is 'template <typename T> class CalcHashClass'.\n");
     13             return _value.hashCode() + 10000;
     14         }
     15     private:
     16         T _value;
     17     };
     18     
     19     //2. int类型实例化特化模板类。
     20     template <>
     21     class CalcHashClass<int> {
     22     public:
     23         CalcHashClass(int const& v) : _value(v) {
     24         }
     25         int hashCode() {
     26             printf("This is 'template <> class CalcHashClass<int>'.\n");
     27             return _value * 101;
     28         }
     29     private:
     30         int _value;
     31     };
     32     
     33     //3. const char*类型实例化的特化模板类
     34     template<>
     35     class CalcHashClass<const char*> {
     36     public:
     37         CalcHashClass(const char* v) {
     38             _v = new char[strlen(v) + 1];
     39             strcpy(_v,v);
     40         }
     41         ~CalcHashClass() {
     42             delete [] _v;
     43         }
     44         int hashCode() {
     45             printf("This is 'template <> class CalcHashClass<const char*>'.\n");
     46             int len = strlen(_v);
     47             int code = 0;
     48             for (int i = 0; i < len; ++i)
     49                 code += (int)_v[i];
     50             return code;
     51         }
     52     
     53     private:
     54         char* _v;
     55     };
     56     
     57     //4. 辅助函数,用于帮助调用者通过函数的参数类型自动进行类型推演,以让编译器决定该
     58     //实例化哪个模板类。这样就可以使调用者不必在显示指定模板类的类型了。这一点和多态有
     59     //点儿类似。
     60     template<typename T>
     61     inline int CalcHashCode(T v) {
     62         CalcHashClass<T> t(v);
     63         return t.hashCode();
     64     }
     65     
     66     //5. 给出一个范例类,该类必须包含hashCode方法,否则将造成编译错误。
     67     class TestClass {
     68     public:
     69         TestClass(const char* v) {
     70             _v = new char[strlen(v) + 1];
     71             strcpy(_v,v);
     72         }
     73         ~TestClass() {
     74             delete [] _v;
     75         }
     76     public:
     77         int hashCode() {
     78             int len = strlen(_v);
     79             int code = 0;
     80             for (int i = 0; i < len; ++i)
     81                 code += (int)_v[i];
     82             return code;
     83         }
     84     private:
     85         char* _v;
     86     };
     87     
     88     int main() {
     89         TestClass tc("Hello");
     90         CalcHashClass<TestClass> t1(tc);
     91         printf("The hashcode is %d.\n",t1.hashCode());
     92         //这里由于为模板类TestClass提供了基于int类型的模板特化类,因此编译器会自动选择
     93         //更为特化的模板类作为t2的目标类。
     94         CalcHashClass<int> t2(10);
     95         printf("The hashcode is %d.\n",t2.hashCode());
     96     
     97         //在上面的示例中,我们通过显示的给出类型信息以实例化不同的模板类,这是因为模板类
     98         //的类型信息是无法像模板函数那样可以通过函数参数进行推演的,为了弥补这一缺失,我们可以
     99         //通过一个额外的模板函数来帮助我们完成这一功能。事实上,这一技巧在Thinking in Java中
    100         //也同样给出了。
    101         printf("Ths hashcode is %d.\n",CalcHashCode(10));
    102         printf("Ths hashcode is %d.\n",CalcHashCode("Hello"));
    103         return 0;
    104     }
    105     //This is 'template <typename T> class CalcHashClass'.
    106     //The hashcode is 10500.
    107     //This is 'template <> class CalcHashClass<int>'.
    108     //The hashcode is 1010.
    109     //This is 'template <> class CalcHashClass<int>'.
    110     //Ths hashcode is 1010.
    111     //This is 'template <> class CalcHashClass<const char*>'.
    112     //Ths hashcode is 500.

        通过上面的示例可以看出,模板特化是依赖于编译器在编译期动态决定该使用哪个特化类,或是标准模板类的。相比于多态的后期动态绑定,该方式的运行效率更高,同时灵活性也没有被更多的牺牲。
        下面将给出一个结合模板特化和多态的示例(请注意代码中的说明性注释)

     1     #include <stdio.h>
     2     #include <string.h>
     3     
     4     //1. 定义一个接口
     5     class BaseInterface {
     6     public:
     7         virtual ~BaseInterface() {}
     8         virtual void doPrint() = 0;
     9     };
    10     
    11     //2. 标准模板类继承该接口,同时给出自己的doPrint()实现。
    12     template<typename T>
    13     class DeriveClass : public BaseInterface {
    14     public:    
    15         void doPrint() {
    16             printf("This is 'template<typename T> class DeriveClass'.\n");
    17         }
    18     };
    19     
    20     //3. 基于int类型特化后的DeriveClass模板类,同样继承了该接口,也给出了自己的DoPrint()实现。
    21     template<>
    22     class DeriveClass<int> : public BaseInterface {
    23     public:    
    24         void doPrint() {
    25             printf("This is 'template<> class DeriveClass<int>'.\n");
    26         }
    27     };
    28     
    29     //4. 对象创建辅助函数,该函数可以通过参数类型的不同,实例化不同的接口子类。
    30     template<typename T>
    31     inline BaseInterface* DoTest(T t) {
    32         return new DeriveClass<T>;
    33     }
    34     
    35     int main() {
    36         BaseInterface* b1 = DoTest(4.5f);
    37         b1->doPrint();
    38         BaseInterface* b2 = DoTest(5);
    39         b2->doPrint();
    40         delete b1;
    41         delete b2;
    42         return 0;
    43     }
    44     //This is 'template<typename T> class DeriveClass'.
    45     //This is 'template<> class DeriveClass<int>'.    

       
    二、模板部分特化:

        有的书中将其翻译成模板偏特化,或者是模板的局部特化,但含义都是相同的。为了便于理解,我们可以将上面的模板特化称为模板全部特化,即模板类的类型参数全部被特化了。顾名思义,模板部分特化只是将其中一部分类型参数进行了特化声明,因此也可以将模板特化视为模板部分特化的一种特殊形式。由于应用场景基本相同,因此下面的代码将仅仅给出最基本的示例和注释说明,以帮助大家熟悉他的语法即可:

     1     //1. 标准模板类。
     2     template<typename T1, typename T2>
     3     class MyClass {
     4         ... ...
     5     };
     6     //2. 两个模板参数具有相同类型的部分特化类。
     7     template<typename T>
     8     class MyClass<T,T> {
     9         ... ...
    10     }
    11     //3. 第二个类型参数是int
    12     template<typename T>
    13     class MyClass<T,int> {
    14         ... ...
    15     }
    16     //4. 两个模板参数都是指针。
    17     template<typename T1,typename T2>
    18     class MyClass<T1*,T2*> {
    19         ... ...
    20     }
    21     //5. 两个模板参数都是相同类型的指针。
    22     template<typename T>
    23     class MyClass<T*,T*> {
    24         ... ...
    25     }
    26     //6. 调用示例代码。
    27     int main() {
    28         MyClass<int,float> c1;         //调用MyClass<T1,T2>
    29         MyClass<float,float> c2;    //调用MyClass<T,T>
    30         MyClass<float,int> c3;      //调用MyClass<T,int>
    31         MyClass<int*,float*> c4;    //调用MyClass<T1*,T2*> 
    32         MyClass<int*,int*> c5;      //调用MyClass<T*,T*>
    33         return 0;
    34     }

       
    三、缺省模板实参:

        和函数的缺省参数一样,C++的模板也同样支持缺省类型参数。

     1     //1. 第二个类型参数的缺省值是vector<T>
     2     template<typename T, typename T2 = std::vector<T> >
     3     class MyClass {
     4         ... ... 
     5     }
     6     int main() {
     7         MyClass<int> c1;            //第二个类型参数是vector<int> 
     8         MyClass<int,list<int> > c2; //第二个类型参数是list<int> 
     9         return 0;
    10     }

        这种使用缺省模板参数的代码,在STL中比比皆是。
        
    四、非类型模板参数:

        模板的类型参数不仅仅可以是类型,也可以是常量,但是常量本身的类型是有限制的,不是所有类型的常量都可以,目前只是整型常量和外部链接对象的指针可以,而浮点型等其他原始类型,或自定义类型均不可。

     1     template<typename T, int MAXSIZE>
     2     class MyContainer {
     3     public:
     4         int capacity() const { return MAXSIZE; }
     5         ... ...
     6     private:
     7         T elements[MAXSIZE];
     8     };
     9      
    10     int main() {
    11         MyContainer<int,50> c1;
    12         return 0;
    13     }
    14     和普通类型模板一样,非类型模板参数也可以有缺省值,如:
    15     template<typename T, int MAXSIZE = 10>
    16     class MyContainer {
    17     public:
    18         int capacity() const { return MAXSIZE; }
    19         ... ...
    20     private:
    21         T elements[MAXSIZE];
    22     };

       
        最后需要说明的是,不管是普通模板类还是非类型模板类,只要其类型不同,或是常量值不同,就不能将其视为相同类型的对象,这一点同样适用于模板函数。

  • 相关阅读:
    Cordova原理一
    View 的measure 和onMeasure
    Android Material Design 系列之 SnackBar详解
    android 透明状态栏方法及其适配键盘上推(二)
    android 透明状态栏方法及其适配键盘上推(一)
    Https握手协议以及证书认证
    App对接支付宝移动支付功能
    ViewPager 滑动一半的判断方法以及左滑右滑判断
    mvp架构解析
    解决IE8打开默认弹出开发者工具的问题
  • 原文地址:https://www.cnblogs.com/orangeform/p/2599400.html
Copyright © 2011-2022 走看看