如何实现一个通用的交换函数?
void Swap(int& left, int& right)
{
int temp = left;
left = right;
right = temp;
}
void Swap(double& left, double& right)
{
double temp = left;
left = right;
right = temp;
}
....
1,函数重载虽然可以实现不同类型的交换,但是代码复用率比较低,只要有新类型出现时,就需要用户自己手动增加对应的函数
2,代码可维护性低
所以能不能写一个模板,让编译器直接用这一个模板完成上述的函数重载任务,答案是可以的。
什么叫泛型编程?
不再是针对某种类型,能适应广泛的类型,跟具体类型无关的代码。
函数模板
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时模板函数根据参数类型推演生成对应类型的函数。
template<typename T1, typename T2,…,typename Tn>
注意:
typename是用来定义模板参数关键字,也可以使用class(切记:不能使用struct代替class)
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
模板的声明与实例化
**模板的声明:**模板的声明并未给出一个函数或者类的完全定义,只是提供了一个函数或者类的语法框架。
模板的实例化而调用模板函数其实调用的是从模板构建出的一个正真的函数或类的过程,下面举了一个swap函数的模板实例化
模板的实例化分显示实例化和隐式实例化
eg:
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 2;
double b = 2.2;
cout << Add(a, b) << endl;
//cout << Add<double>(a, b) << endl;
return 0;
}
如果出现上述代码
该语句不能通过编译,因为在编译期间,当编译器看到该实例化时,需要推演其实参类型,通过a将T推演为int,通过实参b将T推演为double,但模板参数列表中只有一个T。
所以此时编译器无法确定此处T究竟是何种类型,故而报错。
这就是隐式实例化
而且需要
注意:在模板中,编译器一般不会进行类型转换操作,而非模板函数有时会进行类型转换操作!
eg
int Add(int a, int b)
{
return a + b;
}
int main()
{
int a = 2;
double b = 2.2;
cout << Add(a, b) << endl;
//cout << Add<double>(a, b) << endl;
return 0;
}
上述代码是不会报错,直接将此时的b截取成2
如果是显式实例化的话,我们直接在函数名后的<>中指定模板参数的实际类型
eg
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a = 2;
double b = 2.2;
cout << Add<double>(a, b) << endl;
如果类型不匹配,编译器会尝试进行隐式类型转换,如果还是无法转化成功,编译器将会报错。
注
有几个模板参数代表有几个类型
而且当非模板函数和一个同名的函数模板同时存在时,该函数模板还是可以被实例化为这个非模板函数的,但是当条件相同时,编译器还是会优先调用非模板函数而不会从模板函数中实例化产生一个实例,如果模板还能产生一个更好匹配的函数,那么编译器还是会选择模板。
总结:编译器会调用最适合自己的那个函数!
类模板
格式:
template<class T1, class T2, …, class Tn>
class 类模板名
{
// 类内成员定义
};
以动态顺序表vector为例
namespace zjt
{
template<class T>
class vector
{
public:
//构造函数
vector(size_t capacity = 15)
:_start(new T(_capacity))
, _size(0)
,_capacity(capacity)
{
}
private:
T* _start;
size_t _size;
size_t _capacity;
};
}
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟<>,然后将实例化的类型放在<>中即可,类模板名字不是真正的类,而实例化的结果才是真正的类
// Vector类名,Vector<int>才是类型
Vector<int> s1;
Vector<double> s2;
注
模板参数中是允许存在普通变量的
egtemplate<class T, size_t N = 10>
在接下来的模板中可以将N当作常数使用
注意:
- 浮点数、类对象以及字符串是不允许作为非类型模板参数的。
- 非类型的模板参数必须在编译期就能确认结果。
模板的分离编译
什么是分离编译
一个程序(项目)由若干个源文件共同实现,而每个源文件单独编译生成目标文件,最后将所有目标文件链接起来形成单一的可执行文件的过程称为分离编译模式
假设有如下场景:
参考C++入门——下卷(函数重载+内联函数详解)
解决方法:
1.显式实例化指定,在定义的时候直接指定类型。(不推荐使用)
2.直接将声明和定义放到一个文件中并且后缀使用.hpp
总结:
模板优点:
- 模板复用了代码,节省资源,更快的迭代开发,C++的标准模板库(STL)因此而产生
- 增强了代码的灵活性
缺点: - 模板会导致代码膨胀问题,也会导致编译时间变长
- 出现模板编译错误时,错误信息非常凌乱,不易定位错误