C++ 模板(Templates)完整介绍
模板是 C++ 中一种强大的泛型编程机制,允许开发者编写与类型无关的代码,从而提高代码的复用性和灵活性。
通过模板,可以避免为不同数据类型重复编写相似的函数或类,实现真正的代码复用。
1. 模板的基本概念
1.1 为什么需要模板?
- 避免代码重复:传统函数或类需要为不同数据类型(如
int
、double
、string
)重复实现相同逻辑。 - 类型安全:模板在编译时生成特定类型的代码,避免运行时类型错误。
- 提高效率:减少类型转换和重复代码,提升性能。
1.2 模板的两种形式
- 函数模板:适用于函数,可以处理多种数据类型。
- 类模板:适用于类,可以定义通用的类结构。
2. 函数模板
2.1 基本语法
template <typename T> // 或 template <class T>
T max(T a, T b) {return (a > b) ? a : b;
}
template <typename T>
:声明一个模板,T
是类型参数(可以是int
、double
、string
等)。T max(T a, T b)
:定义一个泛型函数,参数和返回值类型均为T
。
2.2 使用示例
int main() {int a = 5, b = 10;double x = 3.14, y = 2.71;cout << max(a, b) << endl; // 输出:10(自动推导 T=int)cout << max(x, y) << endl; // 输出:3.14(自动推导 T=double)
}
- 自动类型推导:编译器根据参数类型自动推导
T
的具体类型。 - 显式指定类型:
cout << max<int>(3.14, 2.71); // 强制 T=int,结果截断为 3
2.3 多个类型参数
template <typename T, typename U>
T min(T a, U b) {return (a < b) ? a : b;
}
- 用途:适用于参数类型不同的情况(如
int
和double
比较)。
3. 类模板
3.1 基本语法
template <typename T>
class Stack {
private:vector<T> data;
public:void push(T value) {data.push_back(value);}T pop() {T value = data.back();data.pop_back();return value;}
};
template <typename T>
:声明一个类模板,T
是类型参数。vector<T>
:使用泛型类型T
定义成员变量。
3.2 使用示例
int main() {Stack<int> intStack; // 实例化一个存储 int 的 StackStack<string> strStack; // 实例化一个存储 string 的 StackintStack.push(42);strStack.push("Hello");cout << intStack.pop() << endl; // 输出:42cout << strStack.pop() << endl; // 输出:"Hello"
}
- 显式指定类型:必须明确指定
T
的具体类型(如Stack<int>
)。
3.3 模板特化(Template Specialization)
- 作用:为特定类型提供定制化的实现。
- 示例:
template <>
class Stack<bool> { // 针对 bool 类型的特化版本
private:vector<int> data; // 用 int 存储 bool(节省空间)
public:void push(bool value) {data.push_back(value ? 1 : 0);}bool pop() {int value = data.back();data.pop_back();return value != 0;}
};
- 用途:优化特定类型的性能或行为(如
bool
类型)。
4. 模板的编译与实例化
4.1 编译过程
- 模板定义:编译器看到模板代码,但不立即生成代码。
- 实例化:当使用模板时(如
max(3, 5)
或Stack<int>
),编译器根据具体类型生成对应的代码。 - 类型检查:编译器检查类型是否合法(如
max("a", "b")
会报错,因为string
没有>
运算符)。
4.2 显式实例化
template class Stack<double>; // 强制编译器生成 Stack<double> 的代码
- 用途:减少编译时间,或确保某些类型被编译。
5. 模板的优缺点
5.1 优点
- 代码复用:一套代码处理多种数据类型。
- 类型安全:编译时检查类型,避免运行时错误。
- 性能优化:无类型转换开销,接近手写代码的效率。
5.2 缺点
- 编译时间增加:模板代码在每次使用时都会实例化,可能导致编译变慢。
- 错误信息复杂:模板错误通常难以阅读(尤其是多层嵌套时)。
- 不支持运行时多态:模板是编译时多态,无法像虚函数那样在运行时动态绑定。