C++模板小结

面向对象和泛型编程在C++的编程思想中占据绝对的地位,而模板又是泛型编程的主要内容。

函数模板

1. 普通模板

//以Swap名称为例
template <typename T0,typename T1> //or class
void Swap(T0 &a, T1 &b)
{......}

2. 具体化

隐式实例化、显式实例化、显式具体化统称为具体化。

(1) 显式具体化(可以写成两种形式)

//以Swap名称为例
template <> 
void Swap<int>(int &a, int &b) 
{......}
//or
/*
template <> 
void Swap(int &a, int &b) //<int>是可选的
{......} */

(2)显式实例化

在模板声明中用符号‘<>’显式的指出类名。

//以Swap名称为例
template void Swap<int>(int , int );

 (3)隐式实例化

依赖输入实参类型实例化模板。

 

//以Swap名称为例
//
模板声明 template <Typename T0, typename T1> void Swap(T0 &, T1 &) //程序入口 void main() { int a = 1; int b = 2; Swap(a, b);//隐式实例化为函数定义void Swap(int &, int &){} ........ }

 

3. 使用哪个函数版本

对于函数重载、函数模板、函数模板重载,C++有一个良好的策略,决定函数调用使用哪一个函数定义。这个过程称作重载解析

对此,编译器需要决定哪一个可行函数是最佳的,从最佳到最差的顺序如下:

一、完全匹配,常规函数优先于模板;

二、提升转换(如char,short —> int或float —> double)

三、标准转换(数值类型之间的非提升转换)

四、用户(用户指使用模板定义的程序员)定义的转换(?用户定义的转换是什么,怎么表示?如何直观地验证其优先级最低?)

(1)完全匹配和最佳匹配

完全匹配允许一些无关紧要的转换(如数组和指针、引用和变量之间)

有两个函数完全匹配是一种错误,但这一规则有两个例外:

一个是指向非const的引用或指针优先与非const指针或引用匹配,其次与const指针或引用匹配(注:只限于引用或指针);

另一个是当两个函数完全匹配时将选择最具体(需要进行的转换最少)的版本。

(2)创建自定义选择

(?)在函数调用中显式地使用符号‘<>’使模板函数具体化。(?)

 

类模板

1. 类模板语法规则

(1)类声明和定义

如果在类声明中定义了方法(内联),则可以省略模板前缀和类限定符;

不能将模板成员函数放在独立的实现文件中。

 1 //a.h
 2 #ifndef A_H_
 3 #define A_H_
 4 #include<iostream>
 5 using namespace std;
 6 
 7 template<class T>
 8 class A
 9 {
10     T t;
11 public:
12     A() : t(0){}
13     void show(){std::cout << t << std::endl;} //内联
14     void view();//模板类方法声明
15 };
16 
17 template<class T>//模板类方法定义
18 void A<T>::view()
19 {
20     std::cout << "view T: " << t << std::endl;
21 } 
22 
23 #endif

(2)模板类的使用

 1 #include"a.h"
 2 
 3 void show(int &);
 4 
 5 void main()
 6 {
 7     A<int> a;
 8     a.show();
 9     system("pause");
10 }

2. 模板的参数

(1)使用非类型参数

模板中由typename或class限定的类型称作类型参数;已知类型称作非类型(或表达式)参数。

template<typename T, int n>  // T为类型参数,n为非类型参数。

非类型参数可以是整型、枚举、引用或指针。(double m是不合法的,但double &和double*是合法的)

模板代码不能修改参数的值,也不能使用参数的地址。

实例化模板时,用作非类型参数的值必须是常量表达式(比如不能把变量赋给模板类array<int, n>中的非类型参数n,array类相对于vector类运行速度快,适合处理大量小数组)。

(2)具体化的模板类可以作为其它模板的类型参数

Array < Stack<int> > asi //外层尖括号内侧最好用空格隔开

(3)可以将具体化的模板用作自身的类型参数,达到递归的目的。

1 typedef array ArrayTP;
2 ArrayTP< ArrayTP<int, 5>, 10 > twodee; //等价于int twodee[10][5]

(4)将模板用作参数

模板可以包含本身就是模板的参数。

1 template< template<typename T> class Thing >
2 class Crab
3 {
4     ......
5 };

(5)使用多个类型参数

模板可以使用多个类型参数。

(6)给模板参数提供默认值

可以给类模板参数提供默认值,但不能给函数模板参数提供默认值,可以为非类型参数提供默认值。

1 template<class T1, class T2 = int, int n = 0>
2 class ToPo
3 {
4 }

 

3. 类模板的具体化

(1)隐式实例化

ArrayTP<double, 100> stuff;

(2)显式实例化

template class ArrayTP<string, 30>;

(3)显式具体化

当对特殊类型实例化时,要对模板进行修改,使其行为不同。这种情况下可以创建显式具体化。

template <> class Classname<specialized-type-name>{......};

(4)部分具体化

//普通模板
template <class T1, class T2> class Pair{......};
//具体化T2为int
template <class T1> class Pair<T1, int>{......};

4. 模板成员

我们提到过数据成员,函数成员,这里引入模板成员。

模板可以作为结构、类和模板类的成员。

5. 模板类和友元

模板类的声明也可以有友元。模板的友元分三类:

1. 非模板友元(类似于普通类的友元);

2. 约束模板友元

使得模板类的每一个具体化,都获得与友元匹配的具体化。包含以下三部:

(1)在类定义的前面声明每个模板函数;

(2)在函数中再次将模板声明为友元;

(3)为友元函数提供模板定义。

3. 非约束模板友元

对于非约束化模板友元,友元模板类型参数与类模板类型参数是不同的。

在类定义中声明模板函数,并声明其为友元;之后再为模板函数提供友元定义。

 

posted @ 2017-10-06 22:48  sungnox  阅读(174)  评论(0)    收藏  举报