C++ 模板基础:函数模板与类模板实战
泛型编程简介
在开发中,我们经常遇到需要处理不同类型但逻辑相同的场景。比如交换两个整数、两个浮点数,甚至两个自定义对象。如果为每种类型都写一遍 Swap 函数,不仅代码冗余,维护成本也高。
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; }
这种'模具填充'的方式就是泛型编程的核心思想:编写与类型无关的通用代码。C++ 中的模板(Template)正是实现泛型编程的基础工具。
一、函数模板
1. 格式与原理
函数模板的基本结构如下:
template<typename T1, typename T2, ..., typename Tn>
返回类型 函数名 (参数列表) {
// 函数体
}
注意,typename 是定义模板参数的关键字,虽然也可以用 class 替代,但通常建议用 typename 以示区分。模板参数的作用域仅限于紧随其后的 {} 块。
下面是一个通用的 Swap 实现:
template<typename T>
void Swap(T& left, T& right) {
T temp = left;
left = right;
right = temp;
}
template<typename T1, typename T2>
T1 Func(const T1& x, const T2& y) {
cout << x << " " << y << endl;
return x;
}
调用时,编译器会根据实参自动推导出模板参数的实际类型,这个过程称为实例化。
int main() {
int a = 0, b = 1;
double c = 1.1, d = 2.2;
Swap(a, b);
Swap(c, d);
// Date d1(1949, 10, 1), d2(2015, 7, 3);
// Func(1, 2);
// Func(1, 2.2);
return 0;
}
实际上,C++ 标准库已经提供了 swap 函数,日常开发中直接调用即可,无需重复造轮子。
2. 函数模板的实例化
实例化主要有两种方式:
- 隐式实例化:编译器根据实参类型自动推导。
- 显示实例化:在函数名后的 <> 中显式指定类型。
template<typename T>
T Add(const T& left, const T& right) {
return left + right;
}
template<typename T>
T* Alloc(int n) {
return new T[n];
}
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, d1) << endl; // 报错:无法确定 T
// 方案 1:用户强制转换
cout << Add(a1, (int)d1) << endl;
cout << Add((double)a1, d1) << endl;
// 方案 2:显示实例化
cout << Add<int>(a1, d1) << endl;
cout << Add<double>(a1, d1) << endl;
// 某些情况只能显示实例化
double* p1 = Alloc<double>(10);
return 0;
}
注意,类型转换会产生临时变量。例如 Add(a1, (int)d1) 中,(int)d1 是右值,所以参数最好加 const 引用以避免不必要的拷贝。
二、类模板
类模板的声明与函数模板类似,但在使用上有些区别。
template<class T1, class T2, ..., class Tn>
class 类模板名 {
// 成员定义
};
对于普通类,类名即类型;而对于类模板,类名只是模板,必须带上具体类型才构成完整类型,例如 Stack 是模板,Stack 才是类型。
由于构造函数参数可能未包含所有模板参数,编译器无法完全通过构造函数的实参推导出所有模板参数,因此类模板通常都需要显示实例化。
template<class T>
class Stack {
public:
Stack(int capacity = 4) {
_a = new T[capacity];
_capacity = capacity;
_size = 0;
}
void Push(const T& data) {
// CheckCapacity();
_a[_size] = data;
_size++;
}
~Stack() {
if (_a) {
delete[] _a; // 注意:new 对应 delete[]
_a = nullptr;
_capacity = 0;
_size = 0;
}
}
private:
T* _a;
int _capacity;
int _size;
};
int main() {
Stack<int> s1;
Stack<double> s2;
Stack<char> s3;
return 0;
}
关于类模板的声明与定义分离:虽然可以分开写在 .h 和 .cpp 中,但在实际工程中,为了避免链接错误,通常建议将类模板的成员函数定义直接放在头文件中,或者确保实例化可见。
以上就是 C++ 模板的基础知识。掌握模板能显著提升代码复用率,减少重复劳动。在实际项目中灵活运用,会让你的设计更加灵活健壮。

