目录
函数模板格式
函数模版原理
函数模板的实例化
模板参数的匹配原则
类模板
函数模板格式
template<typename T1, typename T2,......,typename Tn>
返回值类型 函数名(参数列表){}
template<typename T>
void Swap( T& left, T& right)
{
T temp = left;
left = right;
right = temp;
}
函数模版原理
在编译器编译阶段,对于模板函数的使用,编译器需要根据传入的实参类型来推演生成对应
类型的函数以供调用
函数模板概念
函数模板代表了一个函数家族,该函数模板与类型无关,在使用时被参数化,根据实参类型产生函数的特定类型版本
函数模板的实例化
不同类型的参数使用函数模板时,称为函数模板的实例化
模版不建议声明和定义分离到两个文件.h 和.cpp会出现链接错误
模板参数实例化分为:隐式实例化和显式实例化
隐式实例化(模板实参省略):让编译器根据实参推演模板参数的实际类型 Add(a1, (int)d1)
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2);
Add(d1, d2);
cout << Add(a1, (int)d1) << endl;
return 0;
}
显式实例化:在函数名后的<>中指定模板参数的实际类型 Add<int>(a1, d1)
template<class T>
T Add(const T& left, const T& right)
{
return left + right;
}
int main()
{
int a1 = 10, a2 = 20;
double d1 = 10.0, d2 = 20.0;
Add(a1, a2);
Add(d1, d2);
cout << Add(a1, (int)d1) << endl;
cout << Add<int>(a1, d1) << endl;
return 0;
}
模板参数的匹配原则
1. 一个非模板函数可以和一个同名的函数模板同时存在,而且该函数模板还可以被实例化为这个非模板函数
// 专门处理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版本
}
int main()
{
Test();
}
2. 对于非模板函数和同名函数模板,如果其他条件都相同,在调动时会优先调用非模板函数而不会从该模板产生出一个实例。如果模板可以产生一个具有更好匹配的函数, 那么将选择模板
// 专门处理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); // 模板函数可以生成更加匹配的版本,编译器根据实参生成更加匹配的
}
int main()
{
Test();
}
3. 模板函数不允许自动类型转换,但普通函数可以进行自动类型转换
类模板
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
// 类模版
template<typename T>
class Stack
{
public :
Stack(size_t capacity = 4)
{
_array = new T[capacity];
_capacity = capacity;
_size = 0;
}
~Stack()
{
delete[] _array;
_array = nullptr;
_size = _capacity = 0;
}
void Push(const T& data)
{
if (_size == _capacity)
{
T* tmp = new T[_capacity * 2];
memcpy(tmp, _array, sizeof(T) * _size);
delete[]_array;
_array = tmp;
_capacity *= 2;
}
_array[_size++] = data;
}
private:
T* _array;
size_t _capacity;
size_t _size;
};
int main()
{
//类模板都是显示实例化
Stack<int> s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack<double> s2;
s2.Push(1.1);
s2.Push(2.1);
s2.Push(3.1);
s2.Push(4.1);
}
// 类模版
template<typename T>
class Stack
{
public :
Stack(size_t capacity = 4)
{
_array = new T[capacity];
_capacity = capacity;
_size = 0;
}
~Stack()
{
delete[] _array;
_array = nullptr;
_size = _capacity = 0;
}
void Push(const T& data);
private:
T* _array;
size_t _capacity;
size_t _size;
};
//声明和定义分离时,需要再写一次类模板
template<typename T>
void Stack<T>::Push(const T& data)
{
if (_size == _capacity)
{
T* tmp = new T[_capacity * 2];
memcpy(tmp, _array, sizeof(T) * _size);
delete[]_array;
_array = tmp;
_capacity *= 2;
}
_array[_size++] = data;
}
int main()
{
//类模板都是显示实例化
Stack<int> s1;
s1.Push(1);
s1.Push(2);
s1.Push(3);
s1.Push(4);
Stack<double> s2;
s2.Push(1.1);
s2.Push(2.1);
s2.Push(3.1);
s2.Push(4.1);
}
知识点
template <typename T, int size>
class Array {
public:
T arr[size]; // 使用非类型模板参数
};
template <typename T>
void func(T arg) {
// 函数体
}
//T是一个类型模板参数
可用来创建动态增长和减小的数据结构(模板可以具有非类型参数,用于指定大小,可以根据指定的大小创建动态结构)
模板类型无关,提高了代码复用性
模板运行时不检查数据类型,也不保证类型安全,相当于类型的宏替换
平台无关的,可移植性(只要支持模板语法,模板的代码就是可移植的)
模板实参省略意思为隐式实例化
类模板与模板类所指的不是同一概念(类模板是一个类家族,模板类是通过类模板实例化的具体类)
(
MyClass
是一个类模板,而MyClass<int>
、MyClass<double>
和MyClass<std::string>
是模板类的具体实例)类模板中的成员函数全是模板函数(所有类模板的成员函数,放在类外定义时,需要在函数名前加类名,而类名实际为ClassName<T>,所以定义时还需加模板参数列表)
template<class T> size_t Stack<T>::size(){ return _size; }