模板
1. 泛型编程
如何实现一个通用的交换函数呢?
(使用函数重载可以实现,但是代码重复率比较低,只要有新类型出现时,就需要用户自己增加对应的函数,并且代码可维护性较差,一个出错可能导致所有的重载均出错。)
c++中,存在一个模具,通过给这个模具填充不同的材料(类型),来获得不同材料的铸件(即生成具体类型的代码)。
模板是泛型编程的基础。
模板分为:
1.函数模板
2.类模板
2.函数模板
- 函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本。
语法格式:
template<typename T1 , typename T2,…,typename Tn>
返回值类型 函数名(参数列表){}
(typename 换成class 也可以,但是不能换成struct)
template <class T>
void swap(T& a, T& b)
{
T tmp = a;
a = b;
b = tmp;
}
//模板实现交换函数
原理:
函数模板只是一个蓝图,它本身并不是函数,是编译器用来产生特定具体函数的模具。
int main()
{
int a1 = 10;
int a2 = 20;
double d1 = 10.00;
double d2 = 20.00;
Swap(a1, a2);//编译器在编译时推演为int类型
Swap(d1, d2);//编译器在编译时会推演为double类型
return 0;
}
- 函数模板的实例化
实例化分为:1.隐式实例化 2.显式实例化
对于1. 编译器根据实参推演模板参数的实际类型。
int main()
{
int a1 = 10;
int a2 = 20;
double d1 = 10.00;
double d2 = 20.00;
Swap(a1, a2);//编译器在编译时推演为int类型
Swap(d1, d2);//编译器在编译时会推演为double类型
return 0;
}
需要注意的是:
template <class T>
T Add(T x, T y)
{
return x + y;
}
int main()
{
int a1 = 10;
double d1 = 20.00;
Add(a1, d1);
return 0;
}
这段代码无法通过编译 因为在编译期间,当编译器实例化时,将a1推演为int ,d1推演为double,但模板参数列表只有一个T!编译器无法确定此处到底将T确定为int 还是double。
此时有两种处理方式:
- 强制类型转换
int main()
{
int a1 = 10;
double d1 = 20.00;
Add(a1, (int)d1);
return 0;
}
- 使用显式实例化
对于 显式实例化:
函数名后的<>中指定模板参数的实际类型
int main()
{
int a1 = 10;
double d1 = 20.00;
Add<int>(a1, d1);
return 0;
}
- 模板参数的匹配原则
1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
int Add(int a, int b)
{
return a + b;
}//专门处理 int 加法的函数
template<class T>
T Add(T left, T right)
{
return left + right; //通用的add函数
}
int main()
{
Add(1, 2); //与非模板函数匹配,编译器不需要特化
Add<int>(1, 2); // 调用编译器特化的Add版本
return 0;
}
- 2.对于非模板函数和同名函数模板,如果其他条件相同,在调用时会优先调用非模板函数,而不会从该模板产生出一个实例。 如果模板可以产生一个具有更好匹配的函数,那么将选择该模板。
int Add(int a, int b)
{
return a + b;
}//专门处理 int 加法的函数
template<class T, class Y>
T Add(T left, Y right)
{
return left + right; //通用的add函数
}
int main()
{
Add(1, 2); //与非模板函数匹配,编译器不需要特化
Add<int>(1, 2.0); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的ADD函数
return 0;
}
3.类模板
语法:
template<class T1, class T2,…,class Tn>
class 类名
{ //类内部
}
template<class T >
class Vector
{
public:
Vector(int capacity = 4)
:_a(new T[capacity])
,_size(0)
,_capacity(capacity)
{}
private:
T* _a;
int _size;
int _capacity;
};
类模板函数放在类外面定义时,需要加模板参数列表。
template<class T >
class Vector
{
public:
Vector(int capacity = 4)
:_a(new T[capacity])
,_size(0)
,_capacity(capacity)
{}
~Vector();
void PushBack(T x) {};
private:
T* _a;
int _size;
int _capacity;
};
template<class T>
Vector<T>::~Vector()
{
if (_a)
{
delete[] _a;
_size = _capacity = 0;
}
}
template<class T>
void Vector<T>::PushBack(T x)
{
}
- 类模板实例化
int main()
{
Vector<int> i1;
Vector<double> d2;
return 0;
}
4.模板分离编译:
template<class T>
T Add(T& left, T& right)
{
return left + right;
}// template.h
template<class T>
T Add(const T& left, const T& right);
// test.cpp
int main()
{
Add(1, 2);
return 0;
}
该编译无法通过!
在test.cpp中,编译器没有看到对Add模板函数的实例化,因此不会生成具体的加法函数。 在test.obj中调用 Add(1,2)才会 在链接时寻找其地址,但是这哥函数没有实例化生成的具体代码,因此链接时报错。
解决方案:
1. 将声明和定义放到一个文件 “xxx.hpp" 或 “xxx.h” 中。
2. 模板定义的位置显式实例化(不实用,不推荐)