✨✨欢迎大家来到Celia的博客✨✨
🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉
所属专栏:C++
一、泛型编程
在C语言中,我们想要交换两个变量的值,可以写一个Swap函数,如果我们想要交换不同类型的变量,就需要额外再写一个参数列表不同的Swap函数。在C++中,提供了模板这一方式来更加便利的解决这些问题。泛型编程就是编写与类型无关的通用代码,是代码复用的一种手段。模板是泛型编程的基础。
二、函数模板
2.1 定义格式
定义模板需要用到关键字template,后面跟上<>,在尖括号中包含需要替代的数据类型种类,格式为 class T 或 typename T(不能使用struct T)。T,为指代的类型名,仅仅具有指代作用,可以是任意的名字。如果有多个类型种类,用逗号隔开。
template<class T>
void Swap(T& x1, T& x2)
{
T tmp = x1;
x1 = x2;
x2 = tmp;
}
编译器会根据传入的形参来自动匹配T具体的数据类型。编译器会根据T的类型来根据模板自动生成一个函数。每个模板函数都需要在函数的定义前加上template<class T , ...>。
2.2 使用格式
//函数模板
template<class T>
void Swap(T& x1, T& x2)
{
T tmp = x1;
x1 = x2;
x2 = tmp;
}
int main()
{
int x1 = 2, x2 = 4;
Swap(x1, x2);
cout << x1 << x2;
return 0;
}
2.3 模板实例化
用不同类型的参数使用函数模板时,叫做函数模板的实例化。实例化又分为隐式实例化和显式实例化。
2.3.1 隐式实例化
隐式实例化就是让编译器自动识别模板参数的实际类型,例如:
int main()
{
int x1 = 2, x2 = 4;
Swap(x1, x2);
cout << x1 << x2;
return 0;
}
编译器会根据传入的x1、x2来自动识别模板的实际类型。
2.3.2 显式实例化
显式实例化就是在函数名的后面加上<类型名>来显式指定模板参数的实际类型。例如:
int main()
{
int x1 = 2, x2 = 4;
Swap<int>(x1, x2);
cout << x1 << x2;
return 0;
}
如果传入参数的类型与指定的类型不匹配,编译器会尝试隐式类型转换,如果转换不成功则会报错。
2.4 模板函数的匹配规则
- 模板函数可以和一个非模板的同名函数同时存在。在调用这个函数时,如果不显式实例化,编译器会优先调用非模板函数。
//函数模板 template<class T> void Swap(T& x1, T& x2) { T tmp = x1; x1 = x2; x2 = tmp; } void Swap(int& a, int& b) { int tmp = a; a = b; b = tmp; } int main() { int x1 = 2, x2 = 4; Swap(x1, x2);//调用非模板函数 Swap<int>(x1, x2);//调用模板函数 cout << x1 << x2; return 0; }
- 如果函数模板可以生成更加匹配的模板函数,编译器会优先调用模板函数。
//函数模板 template<class T1, class T2> T1 Add(T1 x1, T2 x2) { return x1 + x2; } //通用函数 int Add(int a, int b) { return a + b; } int main() { int x1 = Add(1, 2);//和通用函数完全匹配,调用通用函数 double x2 = Add(1.0, 2);//函数模板能生成更合适的函数,调用模板函数 return 0; }
三、类模板
3.1 类模板的定义格式
template<class T1, class T2, ..., class Tn>
class 类模板名
{
// 类内成员定义
};
示例:
//类模板(栈)
template<class T>
class Stack
{
public:
Stack(int n = 4)
:_capacity(n),
_size(0),
_a(new T[n])
{}
~Stack()
{
delete[] _a;
}
void Push(T data);
void Pop()
{
_size--;
}
private:
int _capacity;
int _size;
T* _a;
};
template<class T>
void Stack<T>::Push(T data)
{
if (_size == _capacity)
{
T* tmp = new T[_capacity << 1];
memcpy(tmp, _a, sizeof(T) * _capacity);
delete[] _a;
_a = tmp;
tmp = nullptr;
_capacity <<= 1;
}
_a[_size++] = data;
}
int main()
{
Stack<int> s;
s.Push(1);
s.Push(2);
s.Push(3);
s.Push(4);
s.Push(5);
s.Push(6);
return 0;
}
3.2 类模板的注意事项
- 模板类中的成员函数在类外定义时,函数定义前需要加上template<class T>,函数名后需要加上<类型>。
- 类模板的实例化需要显式指定具体类型。实例化后的才是类型名。
// Stack是类名,Stack<int>才是类型 Stack<int> st1; // int Stack<char> st2; // char