【C++】模板初阶
- 一、泛型编程
- 二、函数模板
- 1、函数模板的概念
- 2、函数模板格式
- 3、函数模板的原理
- 4、函数模板的实例化
- 5、模板参数的匹配原则
- 三、类模板
- 1、类模板的定义格式
- 2、类模板的实例化
一、泛型编程
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;
}
在C++中因为有了函数重载,所以我们在给相同功能,不同数据类型的函数命名时可以用同一个,但是还是很麻烦那有办法将其简化吗?
就像活字印刷术相同的模板可以印出内容相同,颜色不同的几个文本
那么C++可以像活字印刷术一样,用于一个模板,创建出功能相同,但是数据类型不同的函数吗?
泛型编程:编写与类型无关的通用代码,是代码复用的一种手段,模板是泛型编程的基础
二、函数模板
1、函数模板的概念
函数模板代表一个函数家族,该函数模板与类型无关,在使用时被实参化,根据实参类型产生函数的特定类型版本。
2、函数模板格式
<1>template<typename T1,typename T2…>
<2>template<class T1, class T2…>
返回值 函数名(参数列表){}
两种都可以
#include<iostream>
using namespace std;template<class T>
void Swap(T& left, T& right)
{T tmp = left;left = right;right = tmp;
}int main()
{int a1 = 1;int a2 = 2;cout << "a1 = " << a1 << " a2 = " << a2 << endl;Swap(a1, a2);cout << "a1 = " << a1 << " a2 = " << a2 << endl;double b1 = 1.1;double b2 = 2.2;cout << "b1 = " << b1 << " b2 = " << b2 << endl;Swap(b1, b2);cout << "b1 = " << b1 << " b2 = " << b2 << endl;return 0;
}
注意typename是模板的关键字,可以用class,但绝对不能把 class 换成struct
3、函数模板的原理
函数模板本身是一个蓝图,并不是一个函数,是编译器用使用方式,产生特定具体类型函数的模板。所以模板其实就是把我们原来做的重复的事交给编译器。
我们到反汇编界面,我们就可以发现编译器在调用函数时 call 的是不同的地址,所以本质是编译器帮我们从一个模板生成了两个不同的函数。
4、函数模板的实例化
用不同类型的参数使用函数模板时,称为函数模板的实例化,实例化分为:隐式实例化,显式实例化
<1> 隐式实例化
让编译器通过实参推演参数的数据数据类型进行实例化
#include<iostream>
using namespace std;template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 1, a2 = 2;double b1 = 1.1, b2 = 2.2;//所谓隐式实例化,就是我们不给编译器指定数据类型//编译器通过我们给的数据类型推演模板的数据类型,再进行实例化cout << Add(a1, a2) << endl;cout << Add(b1, b2) << endl;cout << Add(a1, b1) << endl;//上面这个代码编译器会报错//E0304 没有与参数列表匹配的 函数模板 "Add" 实例//因为我们只设置了一个模板T//在接收到 a1 时推演出 T 为整形//在接收到 b1 时推演出 T 为双精度浮点型//所以编译器无法判断 T 的类型//解决方法//1、强制类型转换 2、显式实例化cout << Add(a1, (int)b1) << endl;cout << Add((double)a1, b1) << endl;return 0;
}
<2> 显式实例化
在函数名后<>指定函数模板的实际数据类型
#include<iostream>
using namespace std;template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{int a1 = 1, a2 = 2;double b1 = 1.1, b2 = 2.2;cout << Add<int>(a1, b1) << endl;cout << Add<double>(a1, b1) << endl;return 0;
}
5、模板参数的匹配原则
<1> 一个非模板函数可以与同名的函数模板同时存在,而且函数模板还可以实例化为非模板函数
#include<iostream>
using namespace std;int Add(const int& left, const int& right)
{return left + right;
}template<class T>
T Add(const T& left, const T& right)
{return left + right;
}int main()
{cout << Add(1, 1) << endl;cout << Add<int>(2, 2) << endl;return 0;
}
从上面的动图可以看出,在没有显式实例化时,函数模板在实例化时会走非模板函数,而在我们显式实例化后,就会走函数模板重新实例化一个。
<2>对于非模板函数和同名函数模板,在条件相同时,调动函数不会走函数模板实例化一个函数。当函数模板可以实例化一个更匹配的函数时,就会走函数模板实例化
#include<iostream>
using namespace std;int Add(int left, int right)
{return left + right;
}template<class T1, class T2>
T1 Add(T1 left, T2 right)
{return left + right;
}int main()
{Add(1, 2);Add(1, 1.1);return 0;
}
三、类模板
1、类模板的定义格式
template<class T1, class T2, class T3, class T4....>
class 类模板名
{};
#include<iostream>
using namespace std;template<class T>
class Stack
{
public://函数声明void Push(const T& date);Stack(T x = 4):_array(new T[x]),_top(0),_capacity(x){}private:T* _array;int _top;int _capacity;
};template<class T>
void Stack<T>::Push(const T& date)
{_array[_top++] = date;
}int main()
{Stack<int> st1;Stack<double> st2;return 0;
}
比之C语言,C++的模板在写顺序表时会方便很多,要是相同场景的C语言要想完成上面一次开辟两个不同数据类型的情况就要写两个顺序表,在命名上就要不一样
2、类模板的实例化
类模板实例化和函数模板实例化不同,类模板实例化必须要在<>中加上参数类型,类模板不是真正的类,实例化的结果是真正的类
Stack<int> st1;
Stack<double> st2;