梦想不会自己发光,真正闪耀的是那个为梦狂奔的你。献给知行的孩子们!(Eric.He著)
本教程将从模板的基本概念、语法规则到进阶应用,全面拆解 C++ 模板的核心用法,帮助你理解泛型编程的思想并灵活运用。
C++ 模板(Template)是泛型编程的核心技术,它允许我们定义 “类型无关” 的函数或类,将数据类型作为参数传递,从而实现代码的复用,避免为不同数据类型编写重复逻辑。
简单来说:模板就像 “代码模具”,只需定义一套逻辑,就能适配 int、float、自定义结构体等多种数据类型,无需重复编写相似代码。
C++ 模板主要分为两类,也是本教程的核心内容:
| 类型 | 作用 |
|---|---|
| 函数模板 | 用于创建类型无关的函数,适配不同类型的参数和返回值; |
| 类、结构体模板 | 用于创建类型无关的类,适配类中的成员变量、成员函数参数及返回值类型。 |
函数模板通过template关键字声明,语法格式如下:
// 模板参数列表:typename/class 关键字声明类型参数,可多个
template <typename T1, typename T2, ...>
返回值类型 函数名(参数列表) {
// 函数逻辑(与类型无关)
}
需求:实现一个通用的交换函数,支持 int、float、字符串等多种类型的数据交换。
#include <iostream>
#include <cstring>
using namespace std;
// 定义函数模板:通用交换函数
template <typename T>
void mySwap(T& a, T& b) {
T temp = a;
a = b;
b = temp;
}
int main() {
// 1. 适配int类型
int a = 10, b = 20;
cout << "交换前:a=" << a << ", b=" << b << endl;
mySwap(a, b);
cout << "交换后:a=" << a << ", b=" << b << endl;
// 2. 适配float类型
float c = 3.14f, d = 6.28f;
cout << "\n交换前:c=" << c << ", d=" << d << endl;
mySwap(c, d);
cout << "交换后:c=" << c << ", d=" << d << endl;
// 3. 适配自定义结构体类型
struct Student {
char name[20];
int age;
};
Student stu1 = {"张三", 18}, stu2 = {"李四", 19};
cout << "\n交换前:stu1.name=" << stu1.name << ", stu2.name=" << stu2.name << endl;
mySwap(stu1, stu2);
cout << "交换后:stu1.name=" << stu1.name << ", stu2.name=" << stu2.name << endl;
return 0;
}
交换前:a=10, b=20
交换后:a=20, b=10
交换前:c=3.14, d=6.28
交换后:c=6.28, d=3.14
交换前:stu1.name=张三, stu2.name=李四
交换后:stu1.name=李四, stu2.name=张三
函数模板本身不生成可执行代码,编译期会根据实际调用时传入的参数类型,生成对应类型的具体函数,这个过程称为模板实例化:
int main() {
int x = 5, y = 15;
// 显式指定T为int类型
mySwap<int>(x, y);
cout << "显式实例化交换后:x=" << x << ", y=" << y << endl;
double m = 2.5, n = 5.0;
// 显式指定T为double类型
mySwap<double>(m, n);
cout << "显式实例化交换后:m=" << m << ", n=" << n << endl;
return 0;
}
当函数需要适配多种不同类型时,可定义多个模板参数:
#include <iostream>
using namespace std;
// 多参数函数模板:T1为第一个参数类型,T2为第二个参数类型,T3为返回值类型
template <typename T1, typename T2, typename T3>
T3 add(T1 a, T2 b) {
return static_cast<T3>(a + b);
}
int main() {
// int + float = double
int a = 10;
float b = 3.14f;
double res1 = add<int, float, double>(a, b);
cout << "10 + 3.14 = " << res1 << endl;
// long + int = long
long c = 100000L;
int d = 50000;
long res2 = add<long, int, long>(c, d);
cout << "100000 + 50000 = " << res2 << endl;
return 0;
}
10 + 3.14 = 13.14
100000 + 50000 = 150000
函数模板可以与普通函数重载,也可以与其他函数模板重载,编译器会按优先级匹配:
#include <iostream>
using namespace std;
// 函数模板:通用加法
template <typename T>
T add(T a, T b) {
cout << "函数模板调用:";
return a + b;
}
// 普通函数:int类型加法
int add(int a, int b) {
cout << "普通函数调用:";
return a + b;
}
// 函数模板重载:多参数版本
template <typename T1, typename T2>
T1 add(T1 a, T2 b) {
cout << "多参数函数模板调用:";
return a + b;
}
int main() {
// 匹配普通函数
cout << add(10, 20) << endl;
// 匹配单参数函数模板(隐式实例化)
cout << add(3.14f, 6.28f) << endl;
// 匹配多参数函数模板
cout << add(10, 3.14f) << endl;
// 强制调用单参数函数模板(显式实例化)
cout << add<int>(10, 20) << endl;
return 0;
}
普通函数调用:30
函数模板调用:9.42
多参数函数模板调用:13.14
函数模板调用:30
C++类模板适用结构体模板,使用方法相同。
类模板用于创建类型无关的类,语法格式如下:
// 模板参数列表
template <typename T1, typename T2, ...>
class 类名 {
public:
// 成员变量(可使用模板类型)
T1 member1;
T2 member2;
// 成员函数(可使用模板类型)
返回值类型 成员函数名(参数列表);
// 成员函数内部实现
void func() {
// 逻辑实现
}
};
// 类模板成员函数的外部实现(必须带模板参数列表和作用域解析符)
template <typename T1, typename T2>
返回值类型 类名<T1, T2<::成员函数名(参数列表) {
// 逻辑实现
}
// 模板参数列表
template <typename T1, typename T2, ...>
struct 结构体名 {
// 成员变量(可使用模板类型)
T1 member1;
T2 member2;
// 成员函数(可使用模板类型)
返回值类型 成员函数名(参数列表);
// 成员函数内部实现
void func() {
// 逻辑实现
}
};
// 结构体名模板成员函数的外部实现(必须带模板参数列表和作用域解析符)
template <typename T1, typename T2>
返回值类型 结构体名<T1, T2>::成员函数名(参数列表) {
// 逻辑实现
}
需求:实现一个通用的栈(Stack)类,支持 int、float、自定义类型的数据存储。
#include <iostream>
#include <vector>
using namespace std;
// 定义类模板:通用栈
template <typename T>
class Stack {
private:
vector<T> data; // 存储栈数据,T为模板类型
public:
// 入栈操作
void push(const T& value) {
data.push_back(value);
cout << value << " 入栈成功" << endl;
}
// 出栈操作
bool pop() {
if (isEmpty()) {
cout << "栈为空,无法出栈" << endl;
return false;
}
cout << data.back() << " 出栈成功" << endl;
data.pop_back();
return true;
}
// 查看栈顶元素
T top() const {
if (isEmpty()) {
throw "栈为空,无栈顶元素"; // 抛出异常
}
return data.back();
}
// 判断栈是否为空
bool isEmpty() const {
return data.empty();
}
// 获取栈的大小
int size() const {
return data.size();
}
};
int main() {
// 1. 实例化int类型的栈
Stack<int> intStack;
cout << "=== int类型栈操作 ===" << endl;
intStack.push(10);
intStack.push(20);
intStack.push(30);
cout << "栈顶元素:" << intStack.top() << endl;
cout << "栈大小:" << intStack.size() << endl;
intStack.pop();
cout << "栈顶元素:" << intStack.top() << endl;
// 2. 实例化string类型的栈
Stack<string> strStack;
cout << "\n=== string类型栈操作 ===" << endl;
strStack.push("Hello");
strStack.push("C++");
strStack.push("Template");
cout << "栈顶元素:" << strStack.top() << endl;
strStack.pop();
cout << "栈顶元素:" << strStack.top() << endl;
// 3. 实例化自定义结构体类型的栈
struct Student {
string name;
int age;
// 重载 << 运算符,便于打印
friend ostream& operator<<(ostream& os, const Student& stu) {
os << "{" << stu.name << ", " << stu.age << "}";
return os;
}
};
Stack<Student> stuStack;
cout << "\n=== Student类型栈操作 ===" << endl;
stuStack.push({"张三", 18});
stuStack.push({"李四", 19});
cout << "栈顶元素:" << stuStack.top() << endl;
stuStack.pop();
cout << "栈顶元素:" << stuStack.top() << endl;
return 0;
}
=== int类型栈操作 ===
10 入栈成功
20 入栈成功
30 入栈成功
栈顶元素:30
栈大小:3
30 出栈成功
栈顶元素:20
=== string类型栈操作 ===
Hello 入栈成功
C++ 入栈成功
Template 入栈成功
栈顶元素:Template
Template 出栈成功
栈顶元素:C++
=== Student类型栈操作 ===
{张三, 18} 入栈成功
{李四, 19} 入栈成功
栈顶元素:{李四, 19}
{李四, 19} 出栈成功
栈顶元素:{张三, 18}
类模板必须显式实例化,即创建对象时必须手动指定模板参数类型,语法:类名<类型> 对象名;(不支持隐式推导)。
int main() {
// 1. 无参构造实例化
Stack<float> floatStack;
floatStack.push(3.14f);
floatStack.push(6.28f);
// 2. 带参构造实例化(若类有带参构造)
// 假设Stack类有带参构造:Stack(int capacity) { ... }
// Stack<double> doubleStack(10); // 初始化栈容量为10
// 3. 指针形式实例化(堆区对象)
Stack<string>* strStackPtr = new Stack<string>();
strStackPtr->push("Test");
cout << "堆区栈顶元素:" << strStackPtr->top() << endl;
delete strStackPtr; // 释放堆区内存
strStackPtr = nullptr;
return 0;
}
类模板支持多个模板参数,也可以为模板参数指定默认值(默认模板参数),简化实例化操作。
#include <iostream>
using namespace std;
// 多参数类模板,指定默认模板参数:T2默认为int,T3默认为string
template <typename T1, typename T2 = int, typename T3 = string>
class MyData {
public:
T1 data1;
T2 data2;
T3 data3;
// 构造函数
MyData(T1 d1, T2 d2, T3 d3) : data1(d1), data2(d2), data3(d3) {}
// 打印数据
void printData() {
cout << "data1: " << data1 << ", data2: " << data2 << ", data3: " << data3 << endl;
}
};
int main() {
// 1. 显式指定所有模板参数
MyData<float, double, string> data1(3.14f, 6.28, "测试1");
cout << "显式指定所有参数:" << endl;
data1.printData();
// 2. 只指定第一个参数,使用默认的T2=int、T3=string
MyData<char> data2('A', 100, "测试2");
cout << "\n使用默认参数:" << endl;
data2.printData();
// 3. 指定前两个参数,使用默认的T3=string
MyData<long, float> data3(100000L, 2.5f, "测试3");
cout << "\n指定前两个参数:" << endl;
data3.printData();
return 0;
}
显式指定所有参数:
data1: 3.14, data2: 6.28, data3: 测试1
使用默认参数:
data1: A, data2: 100, data3: 测试2
指定前两个参数:
data1: 100000, data2: 2.5, data3: 测试3
类模板的成员函数有两种实现方式:
#include <iostream>
#include <vector>
using namespace std;
// 类模板声明
template <typename T>
class Stack {
private:
vector<T> data;
public:
// 成员函数声明
void push(const T& value);
bool pop();
T top() const;
bool isEmpty() const;
int size() const;
};
// 成员函数外部实现:必须带模板参数列表
template <typename T>
void Stack<T>::push(const T& value) {
data.push_back(value);
cout << value << " 入栈成功" << endl;
}
template <typename T>
bool Stack<T>::pop() {
if (isEmpty()) {
cout << "栈为空,无法出栈" << endl;
return false;
}
cout << data.back() << " 出栈成功" << endl;
data.pop_back();
return true;
}
template <typename T>
T Stack<T>::top() const {
if (isEmpty()) {
throw "栈为空,无栈顶元素";
}
return data.back();
}
template <typename T>
bool Stack<T>::isEmpty() const {
return data.empty();
}
template <typename T>
int Stack<T>::size() const {
return data.size();
}
// 测试
int main() {
Stack<int> stack;
stack.push(100);
stack.push(200);
cout << "栈顶元素:" << stack.top() << endl;
stack.pop();
return 0;
}
类模板的声明和实现若分离在.h头文件和.cpp源文件中,会导致编译错误(编译器无法找到模板实例化的代码)。
解决方案:
本教程将从模板的基本概念、语法规则到进阶应用,全面拆解 C++ 模板的核心用法,帮助你理解泛型编程的思想并灵活运用。掌握模板的运用,是实现可复用的数据结构重要基础之一。