大家在做OJ题的时候可能看到过这种使用STL的样子:
其实这个就是我们今天要介绍的模板这个语法在STL中的应用
一.泛型编程
在介绍泛型编程的概念之前,先给大家看一下这个例子
比方说:我今天要分别实现int和int类型,double和double类型,char和char类型的Swap函数
那么我可能需要利用函数重载写出一下三份代码
void Swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } void Swap(double& a, double& b) { double tmp = a; a = b; b = tmp; } void Swap(char& a, char& b) { char tmp = a; a = b; b = tmp;; }
这样也是可以做到的
但是这样好不好呢?
二.函数模板
1.概念
2.实例
因此,我们就可以这样优化那份Swap函数了
//typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class) //template<typename T>//T:type template<class T>//T:type void Swap(T& a, T& b) { T tmp = a; a = b; b = tmp; } int main() { int a = 0, b = 1; Swap(a, b);//模板的实例化 double c = 2.1, d = 3.1; Swap(c, d); char e = 'a', f = 'b'; Swap(e, f); return 0; }
下面问题来了,这三次调用Swap函数调用的是同一个函数吗?
答案是:并不是,为什么呢?
这就涉及到下面的函数模板的原理了
3.原理
因此,如果我的这个函数调用让编译器无法推演出T的类型,也就是下面这种情况,编译器就会报错
(这里我使用Add这个函数来为下面做例子,关于为什么不用Swap函数我会在介绍的时候一并说明的)
注意:在模板中,编译器一般不会进行隐式类型转换操作,
因为一旦转化出问题,编译器就需要背黑锅
能不能通过某种手段来解决这种错误呢?
可以使用下面介绍的函数模板的实例化
4.函数模板的实例化
1.隐式实例化
我们可以这样去做:
这里我通过强制类型转换
分别将i和d这两个变量转换为int和double类型,成功完成了相加
但是:
那么为什么Swap函数就不可以呢?
我们之前在C++入门-引用中提到过:
那么接下来我们来看第二种方法:显式实例化
2.显式实例化
这里里面的int就是告诉编译器,这个T的类型就是int,同理也是如此
那么这种方法能不能解决Swap呢?
当然不行啦,因为我这个Swap的报错是权限放大导致的报错,不是T的类型无法推演出来所导致的报错
注意:
1.当我们无法通过传参的方式来让函数模板推演出T的类型的时候,才是显式实例化真正的用武之地:
2.模板参数列表也可以定义很多T
5.模板参数的匹配原则
1.一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
2. 对于非模板函数和同名函数模板:
如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例
如果模板可以产生一个具有更好匹配的函数 , 那么将选择模板
3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
三.类模板
1.类模板的引出
我们在学习数据结构的时候经常会用到
typedef int STDateType;
等等这样的typedef
但是typedef也不能解决所有问题:
比如这种问题:
如果我今天定义两个栈对象,一个存int,一个存double,这种情况下typedef就无法解决了,
那么该怎么办呢?
这个时候就要用到类模板了
2.实例
语法形式跟函数模板类似
不过:
也就是说这里的真正的类是
Stack Stack Stack Stack
3.易错点:类模板的声明跟定义分离
template<class T> class Stack { public: Stack(int capacity = 4); ~Stack(); //注意:构造函数不能这样写:Stack<T>{...} void Push(const T& val); private: T* _a; int _top; int _capacity; }; //类模板的声明和定义分离:必须这么分离 //但是类模板的声明和定义分离必须放到同一个文件当中 //不能说是:类模板定义的类声明放在.h中,定义放在.cpp中,否则会报错的! template<class T> Stack<T>::Stack(int capacity)//这里指定类域要加上T,而且前面的template<class T>也要加上 :_top(0) , _capacity(capacity) { _a = new T[capacity]; cout << "Stack()" << endl; } template<class T> Stack<T>::~Stack() { delete[]_a; _a = nullptr; _top = _capacity = 0; cout << "~Stack()" << endl; } template<class T> void Stack<T>::Push(const T& val) { //... }
以上就是C++模板初阶的全部内容,希望能对大家有所帮助!