C++(23)—模板初阶
文章目录
- 一、为什么需要泛型编程?
- 二、函数模板
- 1. 函数模板的概念
- 2. 函数模板的语法
- 3. 函数模板的原理
- 4. 函数模板的实例化
- 5. 模板参数匹配规则
- 三、类模板
- 1. 类模板的定义
- 2. 类模板的实例化
- 四、模板的底层原理
- 1. 函数模板的编译过程
- 2. 类模板的编译过程
- 五、模板的优缺点
- 六、实战示例:实现动态数组
一、为什么需要泛型编程?
在C++中,若想实现一个通用的交换函数,传统方式需要为不同类型编写多个重载函数:
void Swap(int& a, int& b) { /*...*/ }
void Swap(double& a, double& b) { /*...*/ }
void Swap(char& a, char& b) { /*...*/ }
问题:
代码冗余:仅类型不同,逻辑完全重复维护困难:新增类型需手动添加函数,易出错解决方案:泛型编程(Generic Programming)核心思想:编写与类型无关的通用代码实现工具:模板(Template)
二、函数模板
1. 函数模板的概念
函数模板是一个“蓝图”,编译器根据传入的实参类型自动生成具体函数。
示例:通用交换函数
template<typename T>
void Swap(T& a, T& b) {T tmp = a;a = b;b = tmp;
}
2. 函数模板的语法
关键字:template<typename T1, typename T2, ...>说明:typename可替换为class(但不可用struct)
3. 函数模板的原理
**编译期代码生成:**模板本身不是函数,编译器根据调用时的类型实例化具体函数。
Swap(d1, d2); // 实例化为 void Swap(double&, double&)
Swap(i1, i2); // 实例化为 void Swap(int&, int&)
4. 函数模板的实例化
隐式实例化
编译器自动推导类型:
Add(1, 2); // T推导为int
Add(1.0, 2.0); // T推导为double
显式实例化
手动指定模板参数类型:
Add<int>(1, 2.5); // T强制为int,2.5隐式转换为int
5. 模板参数匹配规则
优先匹配普通函数:若存在同名非模板函数,优先调用它。更优匹配选择模板:若模板能生成更匹配的版本,则选择模板。
int Add(int a, int b) { return a + b; } // 普通函数
template<typename T1, typename T2>
T1 Add(T1 a, T2 b) { return a + b; } // 模板函数Add(1, 2); // 调用普通函数
Add(1, 2.0); // 调用模板生成的 Add(int, double)
三、类模板
1. 类模板的定义
类模板允许定义与类型无关的通用类,常用于容器类(如动态数组、栈、队列)。
**示例:**通用栈类
template<typename T>
class Stack {
public:Stack(size_t capacity = 4) : _array(new T[capacity]), _capacity(capacity), _size(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) {_array[_size++] = data; // 简化的实现(实际需处理扩容)
}
2. 类模板的实例化
显式实例化:必须指定模板参数类型
Stack<int> intStack; // 存储int类型的栈
Stack<double> doubleStack; // 存储double类型的栈
四、模板的底层原理
1. 函数模板的编译过程
模板解析:检查模板语法是否正确。实例化:根据调用时的类型生成具体函数。编译生成代码:将实例化的函数编译为二进制指令。
2. 类模板的编译过程
模板解析:检查类模板语法。实例化:根据用户指定的类型生成具体的类。编译成员函数:按需编译成员函数(延迟实例化)。
五、模板的优缺点
优点
代码复用:一套逻辑支持多种类型。
类型安全:编译器自动检查类型错误。性能优化:生成的代码与手写代码效率相同。
缺点
编译时间增加:模板实例化会延长编译时间。
代码膨胀:多类型实例化可能导致生成代码体积增大。
六、实战示例:实现动态数组
template<typename T>
class Vector {
public:Vector() : _data(nullptr), _size(0), _capacity(0) {}void PushBack(const T& val) {if (_size == _capacity) {_capacity = _capacity == 0 ? 1 : _capacity * 2;T* newData = new T[_capacity];// 拷贝旧数据(简化实现,未处理异常)for (size_t i = 0; i < _size; ++i) {newData[i] = _data[i];}delete[] _data;_data = newData;}_data[_size++] = val;}
private:T* _data;size_t _size;size_t _capacity;
};int main() {Vector<int> intVec;intVec.PushBack(1);Vector<string> strVec;strVec.PushBack("hello");return 0;
}