目录
思维导图大纲:
1. 什么是模板?
2. 模板的分类
区别:函数模版和模版函数 / 类模版和模版类
2.1 函数模板
2.1.1 用法
2.1.2 原理
2.1.3 函数模板的实例化
2.1.4 模板参数的匹配原则
2.2 类模板
2.2.1 用法
2.2.2 原理
2.2.3 声明和定义分离
2.2.4 类模板的实例化
思维导图大纲:
1. 什么是模板?
模板是泛型编程的一种体现,同一类代码可以多次使用!简单打个比方说,模板就相当于活字印刷术,有了这项技术我们就可以高效的拓印不同的书籍;模板也是如此:有了模版我们可以减少大量重复的代码编写,例如:我们需要写一个swap函数,需求是要交换int类型和float类型等等,如果按照原本函数重载的写法我们仍然需要写多个swap函数,有了模版我们就可以根据不同情况生成不同的swap
2. 模板的分类
区别:函数模版和模版函数 / 类模版和模版类
函数模版是一个模版是一种工具,模版函数是通过模版实例化生成的一个具体的函数对象。同理类模版和模版类也是如此
2.1 函数模板
2.1.1 用法
// 函数模版
template<class T>
void Swap(T& data1, T& data2)
{
T tmp = data1;
data1 = data2;
data2 = tmp;
}
- template关键字
- <>内部是类型,关键字可以使用class/typename
- 返回值类型 函数名(参数列表){}
2.1.2 原理
函数模板是一个蓝图,它本身并不是函数,是编译器用使用方式产生特定具体类型函数的模具。 所以其实模板就是将本来应该我们做的重复的事情交给了编译器
2.1.3 函数模板的实例化
函数模板的实例化分为:
- 隐式实例化:编译器自己去推导类型
- 显示实例化:我们明确类型
// 函数模版
template<class T>
void Swap(T& data1, T& data2)
{
T tmp = data1;
data1 = data2;
data2 = tmp;
}
void Text01()
{
// 函数模版的实例化
int a = 1, b = 2;
double c = 1.1, d = 2.2;
// 1. 显示实例化
Swap<int>(a, b);
Swap<double> (c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
// 2. 隐式实例化
Swap(a, b);
Swap(c, d);
cout << a << " " << b << endl;
cout << c << " " << d << endl;
}
2.1.4 模板参数的匹配原则
- 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这 个非模板函数
- 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而 不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
- 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
2.2 类模板
2.2.1 用法
// 类模版
template<class T>
class Stack
{
public:
void push(const T& data)
{
_v.push_back(data);
}
private:
vector<T> _v;
};
- template关键字
- <>内部是类型,关键字可以使用class/typename
2.2.2 原理
类模板的生成原理和函数模板差不多,但是是属于按需生成(实例化),类模板中的函数也属于函数模板
2.2.3 声明和定义分离
在不同文件中:
类模板的声明和定义不要分离到俩个文件去,会产生连接错误,造成该原因是按需实例化,C++模板的实例化是在编译时进行的,这意味着编译器在编译过程中需要看到模板的完整定义,以便为特定的类型生成实例化代码。如果模板的声明和定义被分开放置在两个文件中,编译器在编译使用模板的源文件时,可能无法找到模板的完整定义,从而导致编译失败或链接错误。
同一个文件中:
需要声明函数的来源使用域访问符::,然后是模板参数
2.2.4 类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的 类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
// Stack是类名,Stack<int>才是类型
Stack<int> st1; // int
Stack<double> st2; // double