我们以顺序表为例来说明,普通顺序表的定义如下:
1 typedef int DataType; 2 //typedef char DataType; 3 class SeqList 4 { 5 private : 6 DataType* _data ; 7 int _size ; 8 int _capacity ; 9 } ;
模板类也是模板, 必须以 关键字templ ate开头, 后接模板形参表。 模板类一般格式如下:
template<class 形参名 1, class 形参名 2, . . . class 形参名 n>
class 类名
{ . . . } ;
1 // 以模板方式实现动态顺序表 2 template<typename T> 3 class SeqList 4 { 5 public : 6 SeqList() ; 7 ~ SeqList() ; 8 private : 9 int _size ; 10 int _capacity ; 11 T* _data ; 12 } ; 13 template <typename T> 14 SeqList <T>: : SeqList() 15 : 16 _size(0) 17 , _capacity(10) 18 , _data(new T[ _capacity] ) 19 {}//注意模板类的类型不是SeqList 20 template <typename T> 21 SeqList <T>: : ~ SeqList() 22 { 23 delete [] _data ; 24 } 25 void test1() 26 { 27 SeqList<int > sl1; 28 SeqList<double > sl2; 29 }
【 模板类的实例化】
只 要有一种不同的类型, 编译器就会实例化出一个对应的类。
SeqList<int > sl1;
SeqList<double > sl2;
当定义上述两种类型的顺序表时, 编译器会使用int和double分别代替模板形参, 重新编写SeqList类, 最后创建名 为SeqList<int>和SeqList<double>的类。
模板参数--实现容器适配器
1 template <typename T> 2 class SeqList 3 {p 4 rivate : 5 int _size ; 6 int _capacity ; 7 T* _data ; 8 } ; 9 // template <class T, class Container> 10 template <class T, class Container = SeqList<T> > // 缺省参数 11 class Stack 12 { 13 public : 14 void Push (const T& x) ; 15 void Pop () ; 16 const T& Top() ; 17 bool Empty () ; 18 private : 19 Container _con ; 20 } ; 21 void Test() 22 { 23 Stack<int> s1; 24 Stack<int , SeqList<int>> s2 ; 25 // 思考下面这种使用场景会怎样? 26 Stack<int , SeqList<char>> s3 ; 27 }
为了避免上述问题的存在用下面的方法:
模板的模板参数--容器适配器
1 template <typename T> 2 class SeqList 3 {p 4 rivate : 5 int _size ; 6 int _capacity; 7 T* _data ; 8 } ; 9 // template <class T, template<class> class Container> 10 template <class T, template<class> class Container = SeqList> // 缺省参数 11 class Stack 12 { 13 public : 14 void Push(const T& x ) ; 15 void Pop() ; 16 const T& Top() ; 17 bool Empty() ; 18 private : 19 Container<T > _con; 20 } ; 21 void Test() 22 { 23 Stack<int> s1; 24 Stack<int , SeqList> s2; 25 }
非类型的类模板参数
1 // 静态顺序表 2 //template<typename T, size_t MAX_SIZE> 3 template <typename T, size_t MAX_SIZE = 10> //带缺省模板参数 4 class SeqList 5 { 6 public : 7 SeqList() ; 8 private : 9 T _array [MAX_SIZE] ; 10 int _size ; 11 } ; 12 template <typename T, size_t MAX_SIZE> 13 SeqList <T, MAX_SIZE>: : SeqList() 14 : _size(0) 15 {} 16 void Test() 17 { 18 SeqList<int> s1; 19 SeqList<int , 20> s2; 20 }
注意: 浮点数和类对象是不允许作为非类型模板参数的。
类模板的特化
类模板的特化:与函数模板类似,当类模板内需要对某些类型进行特别处理时,使用类模板的特化。例如:
1 // general version 2 template<class T> 3 class Compare 4 { 5 public: 6 static bool IsEqual(const T& lh, const T& rh) 7 { 8 std::cout <<"in the general class..." <<std::endl; 9 return lh == rh; 10 } 11 }; 12 // specialize for float 13 template<> 14 class Compare<float> 15 { 16 public: 17 static bool IsEqual(const float& lh, const float& rh) 18 { 19 std::cout <<"in the float special class..." <<std::endl; 20 21 return std::abs(lh - rh) < 10e-3; 22 } 23 }; 24 // specialize for double 25 template<> 26 class Compare<double> 27 { 28 public: 29 static bool IsEqual(const double& lh, const double& rh) 30 { 31 std::cout <<"in the double special class..." <<std::endl; 32 33 return std::abs(lh - rh) < 10e-6; 34 } 35 }; 36 int main(void) 37 { 38 Compare<int> comp1; 39 std::cout <<comp1.IsEqual(3, 4) <<std::endl; 40 std::cout <<comp1.IsEqual(3, 3) <<std::endl; 41 42 Compare<float> comp2; 43 std::cout <<comp2.IsEqual(3.14, 4.14) <<std::endl; 44 std::cout <<comp2.IsEqual(3, 3) <<std::endl; 45 46 Compare<double> comp3; 47 std::cout <<comp3.IsEqual(3.14159, 4.14159) <<std::endl; 48 std::cout <<comp3.IsEqual(3.14159, 3.14159) <<std::endl; 49 return 0; 50 }
全特化
还是以顺序表为例说明
1 全特化 2 template <typename T> 3 class SeqList 4 { 5 public : 6 SeqList() ; 7 ~ SeqList() ; 8 private : 9 int _size ; 10 int _capacity ; 11 T* _data ; 12 } ; 13 template<typename T> 14 SeqList <T>: : SeqList() 15 : _size(0) 16 , _capacity(10) 17 , _data(new T[ _capacity] ) 18 { 19 cout<<"SeqList<T>" <<endl; 20 } 21 template<typename T> 22 SeqList <T>: : ~ SeqList() 23 { 24 delete[] _data ; 25 } 26 template <> 27 class SeqList <int> 28 { 29 public : 30 SeqList(int capacity) ; 31 ~ SeqList() ; 32 private : 33 int _size ; 34 int _capacity ; 35 int* _data ; 36 } ; 37 // 特化后定义成员 函数不再需要模板形参 38 SeqList <int>: : SeqList(int capacity) 39 : _size(0) 40 , _capacity(capacity ) 41 , _data(new int[ _capacity] ) 42 { 43 cout<<"SeqList<int>" <<endl; 44 } 45 // 特化后定义成员 函数不再需要模板形参 46 SeqList <int>: : ~ SeqList() 47 { 48 delete[] _data ; 49 } 50 void test1 () 51 { 52 SeqList<double > sl2; 53 SeqList<int > sl1(2) ; 54 }
1 偏特化( 局部特化) 2 template <typename T1, typename T2> 3 class Data 4 { 5 public : 6 Data() ; 7 private : 8 T1 _d1 ; 9 T2 _d2 ; 10 } ; 11 template <typename T1, typename T2> 12 Data<T1 , T2>: : Data() 13 { 14 cout<<"Data<T1, T2>" <<endl; 15 } 16 // 局部特化第二个参数 17 template <typename T1> 18 class Data <T1, int> 19 { 20 public : 21 Data() ; 22 private : 23 T1 _d1 ; 24 int _d2 ; 25 } ; 26 template <typename T1> 27 Data<T1 , int>: : Data() 28 { 29 cout<<"Data<T1, int>" <<endl; 30 }
下面的例子可以看出, 偏特化并不仅仅是指特化部分参数, 而是针对模板参数更进一步的条件限制所设计出来的一个特化版本。
1 // 局部特化两个参数为指针类型 2 template <typename T1, typename T2> 3 class Data <T1*, T2*> 4 { 5 public : 6 Data() ; 7 private : 8 T1 _d1 ; 9 T2 _d2 ; 10 T1* _d3 ; 11 T2* _d4 ; 12 } ; 13 template <typename T1, typename T2> 14 Data<T1 *, T2*>: : Data() 15 { 16 cout<<"Data<T1*, T2*>" <<endl; 17 } 18 // 局部特化两个参数为引 用 19 template <typename T1, typename T2> 20 class Data <T1&, T2&> 21 { 22 public : 23 Data(const T1& d1, const T2& d2) ; 24 private : 25 const T1 & _d1; 26 const T2 & _d2; 27 T1* _d3 ; 28 T2* _d4 ; 29 } ; 30 template <typename T1, typename T2> 31 Data<T1 &, T2&>: : Data(const T1& d1, const T2& d2) 32 : _d1(d1 ) 33 , _d2(d2 ) 34 { 35 cout<<"Data<T1&, T2&>" <<endl; 36 } 37 void test2 () 38 { 39 Data<double , int> d1; 40 Data<int , double> d2; 41 Data<int *, int*> d3; 42 Data<int&, int&> d4(1, 2) ; 43 }
模板的全特化和偏特化都是在已定义的模板基础之上,不能单独存在。
关于特化的总结:
特化的分类
针对特化的对象不同,分为两类:函数模板的特化和类模板的特化
- 函数模板的特化:当函数模板需要对某些类型进行特化处理,称为函数模板的特化。
- 类模板的特化:当类模板内需要对某些类型进行特别处理时,使用类模板的特化。
特化整体上分为全特化和偏特化
全特化就是模板中模板参数全被指定为确定的类型。
全特化也就是定义了一个全新的类型,全特化的类中的函数可以与模板类不一样。
偏特化
就是模板中的模板参数没有被全部确定,需要编译器在编译时进行确定。
全特化的标志就是产生出完全确定的东西,而不是还需要在编译期间去搜寻适合的特化实现,貌似在我的这种理解下,全特化的 东西不论是类还是函数都有这样的特点,
- 模板函数只能全特化,没有偏特化(重载)。
- 模板类是可以全特化和偏特化的。
很多时候,我们既需要一个模板能应对各种情形,又需要它对于某个特定的类型(比如bool)有着特别的处理,这种情形下特化就是非常必要的。
模板总结
【 优点】
模板复用了 代码, 节省资源, 更快的迭代开发, C++的标准模板库(STL) 因此而产生。
增强了 代码的灵活性。
【 缺点】
模板让代码变得凌乱复杂, 不易维护, 编译代码时间变长。
出现模板编译错误时, 错误信息非常凌乱, 不易定位错误。