CPlusPlusThings/practical_exercises/10_day_practice/day8/readme.md
Light-City a4d828bb4c update
2020-04-06 00:57:02 +08:00

187 lines
6.1 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

模板template是C++实现代码重用机制的重要工具,是泛型技术(即与数据类型无关的通用程序设计技术)的基础。
模板是C++中相对较新的?语言机制,它实现了与具体数据类型无关的通用算法程序设计,能够提高软件开发的效率,是程序代码复用的强有力工具。
本章主要介绍了函数模板和类模板两类以及STL库中的几个常用模板数据类型。
一、模板
1、模板概念
模板是对具有相同特性的函数或类的再抽象,模板是一种参数多态性的工具,可以为逻辑功能相同而类型不同的程序提供一种代码共享的机制。
一个模板并非一个实实在在的函数或类,仅仅是一个函数或类的描述,是参数化的函数和类。
2、模板分类
函数模板
类模板
3.函数模板与模板函数
函数模板提供了一种通用的函数行为,该函数行为可以用多种不同的数据类型进行调用,编译器会据调用类型自动将它实例化为具体数据类型的函数代码,也就是说函数模板代表了一个函数家族。
与普通函数相比,函数模板中某些函数元素的数据类型是未确定的,这些元素的类型将在使用时被参数化;与重载函数相比,函数模板不需要程序员重复编写函数代码,它可以自动生成许多功能相同但参数和返回值类型不同的函数。
二、函数模板
1、函数模板的定义
```c++
template <class T1, class T2,…>返回类型 函数名(参数表){
…… //函数模板定义体
}
```
template是定义模板的关键字写在一对<>中的T1T2…是模板参数其中的class表示其后的参数可以是任意类型。
模板参数常称为类型参数或类属参数在模板实例化即调用模板函数时时需要传递的实参是一种数据类型如int或double之类。
函数模板的参数表中常常出现模板参数如T1T2
2、使用函数模板的注意事项
① 在定义模板时不允许template语句与函数模板定义之间有任何其他语句。
```c++
template <class T>
int x; //错误,不允许在此位置有任何语句
T min(T a,T b){…}
```
② 函数模板可以有多个类型参数但每个类型参数都必须用关键字class或typename限定。此外模板参数中还可以出现确定类型参数称为非类型参数。例
```c++
template <class T1,class T2,class T3,int T4>
T1 fx(T1 a, T 2 b, T3 c){…}
```
在传递实参时非类型参数T4只能使用常量
③ 不要把这里的class与类的声明关键字class混淆在一起虽然它们由相同的字母组成但含义是不同的。这里的class表示T是一个类型参数可以是任何数据类型如int、float、char等或者用户定义的struct、enum或class等自定义数据类型。
④ 为了区别类与模板参数中的类型关键字class标准C++提出?了用typename作为模板参数的类型关键字同时也支持使用class。比如把min定义的template <class T>写成下面的形式是完全等价的:
```c++
template <typename T>
T min(T a,T b){…}
```
3.函数模板的实例化
- 实例化发生的时机
模板实例化发生在调用模板函数时。当编译器遇到程序中对函数模板的调用时,它才会根据调用语句中实参的具体类型,确定模板参数的数据类型,并用此类型替换函数模板中的模板参数,生成能够处理该类型的函数代码,即模板函数。
- 当多次发生类型相同的参数调用时只在第1次进行实例化。假设有下面的函数调用
```c++
int x=min(2,3);
int y=min(3,9);
int z=min(8.5);
```
编译器只在第1次调用时生成模板函数当之后遇到相同类型的参数调用时不再生成其他模板函数它将调用第1次实例化生成的模板函数。
- 实例化的方式
**隐式实例化**
编译器能够判断模板参数类型时,自动实例化函数模板为模板函数
```c++
template <typename T> T max (T, T);
int i = max (1, 2);
float f = max (1.0, 2.0);
char ch = max (a, A);
```
隐式实例化,表面上是在调用模板,实际上是调用其实例
**显示实例化explicit instantiation**
时机,编译器不能判断模板参数类型或常量值
需要使用特定数据类型实例化
语法形式::
模板名称<数据类型,…,常量值,…> (参数)
```c++
template <class T> T max (T, T);
int i = max (1, 2);
// error: data type cant be deduced
int i = max<int> (1, 2);
```
4.函数模板的特化
- 特化的原因
但在某些情况下,模板描述的通用算法不适合特定的场合(数据类型等)
比如如max函数
```c++
char * cp = max (“abcd”, “1234”);
实例化为char * max (char * a, char * b){return a > b ? a : b;}
```
这肯定是有问题的,因为字符串的比较为:
```c++
char * max (char * a, char * b)
{ return strcmp(a, b)>0 ? a : b; }
```
- 特化
所谓特化,就是针对模板不能处理的特殊数据类型,编写与模板同名的特殊函数专门处理这些数据类型。
模板特化的定义形式:
template <> 返回类型 函数名<特化的数据类型>(参数表) {
……
}
说明:
① template < >是模板特化的关键字,< >中不需要任何内容;
② 函数名后的< >中是需要特化处理的数据类型。
5.说明
① 当程序中同时存在模板和它的特化时,特化将被优先调用;
② 在同一个程序中除了函数模板和它的特化外还可以有同名的普通函数。其区别在于C++会对普通函数的调用实参进行隐式的类型转换,但不会对模板函数及特化函数的参数进行任何形式的类型转换。
6.调用顺序
当同一程序中具有模板与普通函数时,其匹配顺序如下:
完全匹配的非模板函数
完全匹配的模板函数
类型相容的非模板函数
三、类模板
1.类模板的概念
类模板可用来设计结构和成员函数完全相同,但所处理的数据类型不同的通用类。
如栈,存在
双精度栈:
```c++
class doubleStack{
private:
double data[size];
……
};
```
字符栈:
```c++
class charStack{
private:
char data[size];
……
};
```
这些栈除了数据类型之外,操作完全相同,就可用类模板实现。
2.类模板的声明
```c++
template<class T1,class T2,…>
class 类名{
…… // 类成员的声明与定义
}
```
其中T1、T2是类型参数
类模板中可以有多个模板参数,包括类型参数和非类型参数
3.非类型参数
非类型参数是指某种具体的数据类型,在调用模板时只能为其提供用相应类型的常数值。非类型参数是受限制的,通常可以是整型、枚举型、对象或函数的引用,以及对象、函数或类成员的指针,**但不允许用浮点型或双精度型、类对象或void作为非类型参数**。
在下面的模板参数表中T1、T2是类型参数T3是非类型参数。
```c++
template<class T1,class T2,int T3>
```
在实例化时必须为T1、T2提供一种数据类型为T3指定一个整常数如10该模板才能被正确地实例化。
4.类模板的成员函数的定义
方法1在类模板外定义语法
template <模板参数列表> 返回值类型 类模板名<模板参数名表>::成员函数名 (参数列表) { …… };
方法2成员函数定义与常规成员函数的定义类似另外
“模板参数列表”引入的“类型标识符”作为数据类型使用
“模板参数列表”引入的“普通数据类型常量”作为常量使用
5.类模板特化
特化
解决例9-9的方法是特化。即用与该模板相同的名字为某种数据类型专门重写一个模板类。
类模板有两种特化方式:一种是特化整个类模板,另一种是特化个别成员函数
特化成员函数的方法:
```c++
template <> 返回类型 类模板名<特化的数据类型>::特化成员函数名(参数表){
…… //函数定义体
}
```
```c++
template<> void Array<char *>::Sort(){
for(int i=0;i<Size-1;i++){
int p=i;
for(int j=i+1;j<Size;j++)
if(strcmp(a[p],a[j])<0)
p=j;
char* t=a[p];
a[p]=a[i];
a[i]=t;
}
}
```