模板
交换函数
实现通用的交换函数。
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;
}
void Swap(char& left, char& right) {
char temp = left;
left = right;
right = temp;
}
缺点
- 重载的函数仅仅是类型不同,代码复用率比较低,只要有新类型出现时,就需要用户自己增加对应的函数。
- 代码的可维护性比较低,一个出错可能所有的重载均出错。
定义
模板是一个'代码蓝图',可以根据传入的类型'生成'对应版本的具体代码。
核心价值
解决'同逻辑、不同类型'的代码冗余问题。
分类
函数模板
定义
定义一个通用函数,参数类型通过模板参数指定,编译器会根据传入的实参类型自动生成对应版本的函数。
格式
template<typename T>
使用 template 和 typename 来声明模板,这里的 typename 也可以使用 class 来代替,不可以使用 struct。T 是参数类型,可以是内置类型或者自定义类型,可以作为函数的参数或者返回值。
多个类型
template<typename T1, typename T2>
代码示例
#include <iostream>
using namespace std;
template<typename T> // T 是模板参数,类似于形参,定义的是类型
void Swap(T& x1, T& x2) {
T tmp = x1;
x1 = x2;
x2 = tmp;
}
int main() {
int a = 0, b = 1;
double c = 1.1, d = 2.2;
Swap(a, b);
cout << a << " " << b << endl;
Swap(c, d);
cout << c << " " << d << endl;
return 0;
}
上面的代码中,2 个 Swap 调用的不是同一个函数,编译器通过类型自动推导出函数。
函数模板的原理
编译器根据参数类型推导出具体函数。
可以直接使用库里面的 swap 模板,库里面的 swap 是小写。
#include <iostream>
using namespace std;
int main() {
int a = 1, b = 2;
cout << a << " " << b << endl;
swap(a, b);
cout << a << " " << b << endl;
return 0;
}
函数模板的实例化
隐式实例化
编译器根据实参推演模板参数的实际类型。
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right) {
return left + right;
}
int main() {
int a1 = 10, a2 = 20;
double d1 = 10.1, d2 = 20.2;
cout << Add(a1, d1) << endl;
return 0;
}
代码报错是因为 a1 和 d1 的类型不同,模板参数列表中只有一个 T,编译器无法确定此处到底该将 T 确定为 int 或者 double 类型而报错,在模板中,编译器一般不会进行类型转换操作。下面有两种解决方法。
方法 1
在参数前面添加强制类型转换。
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right) {
return left + right;
}
int main() {
int a1 = 10, a2 = 20;
double d1 = 10.1, d2 = 20.2;
cout << Add(a1, (int)d1) << endl;
cout << Add((double)a1, d1) << endl;
return 0;
}
不同的强制类型转换,得到的结果是不一样的。
方法 2
显示类型转换,在函数名后的 <> 中指定模板参数的实际类型。
#include <iostream>
using namespace std;
template<class T>
T Add(const T& left, const T& right) {
return left + right;
}
int main() {
int a1 = 10, a2 = 20;
double d1 = 10.1, d2 = 20.2;
// cout << Add(a1, a2) << endl;
// cout << Add(d1, d2) << endl;
// 强制类型转换
// cout << Add(a1, (int)d1) << endl;
// cout << Add((double)a1, d1) << endl;
// 显示实例化
cout << Add<int>(a1, d2) << endl;
// 隐式类型转换
cout << Add<double>(a1, d2) << endl;
return 0;
}
Add 函数中,使用 const 是因为隐式类型转换会产生临时变量,临时变量具有常性。
特殊情况
有些函数无法自动推导,只能显示实例化。
#include <iostream>
using namespace std;
template<typename T>
T* Alloc(int n) {
return new T[n];
}
int main() {
double* p1 = Alloc<double>(10);
return 0;
}
泛型编程
使用模板来完成的,代码针对广泛的类型。
类模板
格式
template<class T1, class T2, ..., class Tn>
class 类模板名 {
// 类内成员定义
};
类模板可以定义多个模版参数。
代码示例
类模板只能显示实例化,不能传递参数。
#include <iostream>
using namespace std;
template<typename T>
class Stack {
public:
Stack(size_t capacity = 3) {
cout << "Stack(size_t capacity = 3)" << endl;
_array = new T[capacity]; // 只需要个数
_capacity = capacity;
_size = 0;
}
void Push(const T& data) {
_array[_size] = data;
_size++;
}
~Stack() {
cout << "~Stack()" << endl;
if (_array) {
delete[] _array;
_array = NULL;
_capacity = 0;
_size = 0;
}
}
private:
T* _array;
int _capacity;
int _size;
};
int main() {
Stack<int> s1;
// Stack<double> s1;
return 0;
}
类模板的实例化
类模板实例化与函数模板实例化不同,类模板实例化需要在类模板名字后跟 <>,然后将实例化的类型放在 <> 中即可,类模板名字不是真正的类,而实例化的结果才是真正的类。
普通类,类名和类型一样,类模板,类名和类型不一样。例如:类名:Stack,类型:Stack。
template< typename T > 的作用范围是下一个函数或者类。
总结
模板的出现,让 C++ 代码摆脱了重复冗余的枷锁,无论是函数模板实现通用逻辑复用,还是类模板构建灵活数据结构,都极大提升了编程效率与代码可维护性。掌握这些知识,就像拿到了 C++ 进阶的密钥,助力我们写出更简洁、更高效、更具扩展性的代码,在编程之路上走得更远、更稳,去探索更广阔的代码天地。

