
◆博主名称:少司府
欢迎来到少司府的博客☆*: .。. o(≧▽≦)o .。.:*☆
⭐数据结构系列个人专栏:
⭐C++基础个人专栏:
⭐琢玉成器终有时,笔底生花夺锦归
目录
[1.1 样例引入](#1.1 样例引入)
[1.2 泛型编程](#1.2 泛型编程)
[2.1 概念与格式](#2.1 概念与格式)
[2.2 模板的原理](#2.2 模板的原理)
[2.3 模板的实例化](#2.3 模板的实例化)
[2.3.1 隐式实例化(推导实例化)](#2.3.1 隐式实例化(推导实例化))
[2.3.2 显式实例化](#2.3.2 显式实例化)
[2.4 模板参数的匹配原则](#2.4 模板参数的匹配原则)
[3.1 类模板的定义格式](#3.1 类模板的定义格式)
[3.2 类模板的实例化](#3.2 类模板的实例化)
[3.3 函数模板、模板函数、类模板、模板类区分](#3.3 函数模板、模板函数、类模板、模板类区分)
1.1 样例引入
当我们学了函数重载之后,我们试想一下,该如何实现一个通用的交换函数呢?利用函数重载吗?
cpp
void Swap(int& left,int& right)
{
int tmp=left;
left=right;
right=tmp;
}
void Swap(double& left,double& right)
{
double tmp=left;
left=right;
right=tmp;
}
void Swap(char& left,char& right)
{
char tmp=left;
left=right;
right=tmp;
}
//...
如图,函数重载可以实现,但是较为复杂,且维护成本较高 ,当有新类型出现时,就需要手动增加重载函数,代码复用率比较低。
1.2 泛型编程
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。

1)、在C++中,存在着一种模具叫做模板 (template),通过模板进行泛型编程来实现通用的函数
2)、模板是半自动化的,通过模板编辑器自动生成需要的函数。
二、函数模板
2.1 概念与格式
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。其本质是一个工具。
cpp
// 模板参数列表<class 类型1,class 类型2,...>
// 函数参数列表(类型 变量1,类型 变量2,...)
如图,模板的参数列表和函数的参数列表类似,其中,class 可以用 typename 替换,两者几乎没区别。
cpp
template<class T>
void Swap(const T& x,const T& y)
{
const T tmp=x;
x=y;
y=tmp
}
如图,是一个交换函数的实现,该模板只能实现同类型的交换,不同类型需要两个参数T1、T2。
C++标准库实现了 swap ,可以直接调用。
2.2 模板的原理
函数模板是一个蓝图,它本身并不是函数,是编译器使用方式产生特定具体类型函数的模具。

编译器在编译阶段 ,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应类型的函数以供调用。比如:当用double 类型使用函数模板时,编译器通过对实参类型的推演,将T确定为double 类型,然后产生一份专门处理double 类型的代码。
2.3 模板的实例化
模板的实例化:用函数模板生成对应的模板函数。
2.3.1 隐式实例化(推导实例化)
cpp
template<class T>
T Add(const T& left,const T& right)
{
return left+right;
}
int main()
{
int a1=10,a2=20;
double b1=10.1,b2=10.2;
cout << Add(a1,(int)b1) << endl; // 推导实例化
return 0;
}
如图,没有显式传T ,推导T的类型,为推导实例化(隐式实例化)。
结构如图:

2.3.2 显式实例化
cpp
cout << Add<int>(a1,b1) << endl;
// 显式实例化
如图,显示传入T 的类型为int。

1)、当T 不为形参中的类型时,无法推导,只能显示实例化
cpp
// e.g.
template<class T>
T* func(int n)
{
return new T[n];
}
2)、模板和非模板同时存在时,优先调用非模板函数
3)、模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
2.4 模板参数的匹配原则
1)、非模板函数和同名的函数模板可以同时存在,且该函数模板还可以被实例化为这个非模板函数
2)、对于非模板函数和同名的模板函数,如果其他条件相同,在调用时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个更好匹配的函数 ,那么就将选择模板。
cpp
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T1, class T2>
T1 Add(T1 left, T2 right)
{
return left + right;
}
void Test()
{
Add(1, 2);
Add(1, 2.0);
}
如图,
对于 Add(1,2),与非函数模板类型完全匹配,不需要函数模板实例化
对于 Add(1,2.0),模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的
Add函数
cpp
// 专门处理int的加法函数
int Add(int left, int right)
{
return left + right;
}
// 通用加法函数
template<class T>
T Add(T left, T right)
{
return left + right;
}
void Test()
{
Add(1, 2);
Add<int>(1, 2);
}
如图,
对于 Add(1,2) ,与非模板函数匹配,编译器不需要特化
对于 Add<int>(1,2),调用编译器特化的Add版本
三、类模板
类模板都是显示实例化。
3.1 类模板的定义格式
cpp
template<class T1,class T2,...>
class 类模板名
{
//...
};
举个栗子:
cpp
template<typename T>
class Stack
{
public:
Stack(size_t capacity = 4)
{
_array = new T[capacity];
_capacity = capacity;
_size = 0;
}
void Push(const T& data);
private:
T* _array;
size_t _capacity;
size_t _size;
};
// 模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
template<class T>
void Stack<T>::Push(const T& data)
{
// 扩容
_array[_size] = data;
++_size;
}
3.2 类模板的实例化
类模板实例化需要在类模板名字后面跟 <>,然后将实例化的类型放在 <> 中,类模板名字不是真正的类,实例化的结果才是。
cpp
// Stack 是类名,Stack<int> 才是类型
Stack<int> st1;
Stack<double> st2;
需要注意的是,对于下面这句代码:
cpp
Stack<double>* pst=new Stack<double>;
delete pst;

注意先后顺序。
3.3 函数模板、模板函数、类模板、模板类区分
|----------|----------------------|
| 函数模板、类模板 | 本质上是模具,不是具体的函数或者具体的类 |
| 模板函数、模板类 | 本质上是产品,是实例化出来的函数、类 |
本期的分享就到这里,如果觉得博主的文章比较对胃口的话,可以点一个小小的关注~
您的三连是我持续更新的动力~