到目前为止,我们已经学会了变量、函数、类、指针、文件操作等核心知识。这一节,我们来解锁 C++ 中两个超级实用的特性:模板(Template) 和 STL(标准模板库)。它们能让你的代码更简洁、更通用、更强大!
1. 为什么需要模板?
想象一下:你写了一个函数用来求两个数的最大值。
cpp
int maxVal(int a, int b) {
return (a > b) ? a : b;
}
很好用!但如果我想求两个 double 的最大值呢?两个 char 的最大值呢?你可能会说:再写几个重载函数呗。
cpp
double maxVal(double a, double b) {
return (a > b) ? a : b;
}
可是这样每多一种类型,就要多写一个函数,太麻烦了。模板就是用来解决这个问题的------你只需要写一次,编译器会自动帮你生成适用于各种类型的版本。
2. 函数模板
2.1 基本语法
函数模板的写法非常简单,在函数前面加上 template <typename T> 就行:
cpp
#include <iostream>
using namespace std;
template <typename T>
T maxVal(T a, T b) {
return (a > b) ? a : b;
}
int main() {
cout << "int: " << maxVal(3, 7) << endl;
cout << "double: " << maxVal(3.14, 2.71) << endl;
cout << "char: " << maxVal('a', 'z') << endl;
return 0;
}
输出:
int: 7
double: 3.14
char: z
这里的 T 就是一个 类型占位符 。当你调用 maxVal(3, 7) 时,编译器发现参数是 int,就把 T 替换成 int;调用 maxVal(3.14, 2.71) 时,T 就变成 double。
你可以把 T 想象成一个"待填写的类型空格",编译器会根据你的调用自动填上正确的类型。
2.2 多个模板参数
模板参数也可以有多个:
cpp
#include <iostream>
using namespace std;
template <typename T1, typename T2>
void printPair(T1 a, T2 b) {
cout << "(" << a << ", " << b << ")" << endl;
}
int main() {
printPair(1, 3.14);
printPair("hello", 42);
printPair('A', true);
return 0;
}
输出:
(1, 3.14)
(hello, 42)
(A, 1)
3. 类模板
函数可以模板化,类当然也可以!
3.1 一个简单的 Pair 类模板
cpp
#include <iostream>
#include <string>
using namespace std;
template <typename T1, typename T2>
class Pair {
public:
T1 first;
T2 second;
Pair(T1 a, T2 b) : first(a), second(b) {}
void print() const {
cout << "(" << first << ", " << second << ")" << endl;
}
};
int main() {
Pair<int, double> p1(1, 3.14);
Pair<string, int> p2("年龄", 18);
p1.print();
p2.print();
return 0;
}
输出:
(1, 3.14)
(年龄, 18)
注意使用类模板时,需要在尖括号里 显式指定类型 ,比如 Pair<int, double>。这和函数模板不同,函数模板通常可以自动推导类型,但类模板一般需要你手动告诉它。
3.2 一个简单的 Stack 类模板
再来一个稍微实用一点的例子------用模板实现一个简单的栈(Stack):
cpp
#include <iostream>
#include <string>
using namespace std;
template <typename T>
class SimpleStack {
private:
T data[100];
int topIndex;
public:
SimpleStack() : topIndex(-1) {}
void push(const T& val) {
if (topIndex >= 99) {
cout << "栈已满!" << endl;
return;
}
data[++topIndex] = val;
}
T pop() {
if (topIndex < 0) {
cout << "栈为空!" << endl;
return T();
}
return data[topIndex--];
}
bool empty() const {
return topIndex < 0;
}
};
int main() {
// 整数栈
SimpleStack<int> intStack;
intStack.push(10);
intStack.push(20);
intStack.push(30);
cout << "弹出: " << intStack.pop() << endl; // 30
cout << "弹出: " << intStack.pop() << endl; // 20
// 字符串栈
SimpleStack<string> strStack;
strStack.push("你好");
strStack.push("世界");
cout << "弹出: " << strStack.pop() << endl; // 世界
cout << "弹出: " << strStack.pop() << endl; // 你好
return 0;
}
输出:
弹出: 30
弹出: 20
弹出: 世界
弹出: 你好
同一个 SimpleStack 类,通过模板就能存放不同类型的数据,这就是模板的威力!
4. 什么是 STL?
STL(Standard Template Library,标准模板库) 是 C++ 自带的一个"工具箱",里面包含了大量常用的数据结构和算法。你可以把它想象成一个超级工具箱 :需要收纳就用 vector,需要查字典就用 map,需要排序就用 sort------都不用自己从零造轮子。
STL 主要由三部分组成:
- 容器(Container) :用来存放数据,比如
vector、map、set - 算法(Algorithm) :用来处理数据,比如
sort、find - 迭代器(Iterator):连接容器和算法的"桥梁"
5. STL 容器:vector
vector 是 STL 中最常用的容器,可以理解为一个自动扩容的数组。
cpp
#include <iostream>
#include <vector>
using namespace std;
int main() {
// 创建一个空的 vector
vector<int> nums;
// 往末尾添加元素
nums.push_back(10);
nums.push_back(20);
nums.push_back(30);
nums.push_back(40);
// 用下标访问
cout << "第一个元素: " << nums[0] << endl;
// 获取大小
cout << "大小: " << nums.size() << endl;
// 遍历(范围 for 循环)
cout << "所有元素: ";
for (int n : nums) {
cout << n << " ";
}
cout << endl;
// 删除最后一个元素
nums.pop_back();
cout << "删除后大小: " << nums.size() << endl;
return 0;
}
输出:
第一个元素: 10
大小: 4
所有元素: 10 20 30 40
删除后大小: 3
和普通数组相比,vector 的好处是:你不需要提前知道要存多少数据,它可以自动增长。push_back 加一个,pop_back 删一个,size() 问大小------非常方便。
6. STL 容器:map
map 就像一本字典 ,它存储的是 键值对(key-value pair):你给它一个"键",它返回对应的"值"。
cpp
#include <iostream>
#include <map>
#include <string>
using namespace std;
int main() {
map<string, int> ages;
// 添加键值对
ages["小明"] = 18;
ages["小红"] = 20;
ages["小刚"] = 19;
// 通过键访问值
cout << "小明的年龄: " << ages["小明"] << endl;
// 查找键是否存在
if (ages.find("小红") != ages.end()) {
cout << "小红存在,年龄: " << ages["小红"] << endl;
}
// 遍历 map
for (const auto& pair : ages) {
cout << pair.first << " -> " << pair.second << endl;
}
return 0;
}
输出:
小明的年龄: 18
小红存在,年龄: 20
小刚 -> 19
小明 -> 18
小红 -> 20
map 内部会自动按键排序(这里按字符串的字典序),查找速度也很快。适合需要"通过名字找数据"的场景。
7. STL 容器:set
set 是一个自动去重的有序集合。你往里面丢元素,它会自动帮你排好序并且去掉重复的。
cpp
#include <iostream>
#include <set>
using namespace std;
int main() {
set<int> s;
s.insert(30);
s.insert(10);
s.insert(20);
s.insert(10); // 重复,不会插入
s.insert(30); // 重复,不会插入
cout << "大小: " << s.size() << endl; // 3
cout << "元素: ";
for (int n : s) {
cout << n << " ";
}
cout << endl;
// 查找
if (s.count(20)) {
cout << "20 存在" << endl;
}
return 0;
}
输出:
大小: 3
元素: 10 20 30
20 存在
set 非常适合需要"不重复"和"自动排序"的场景,比如记录一组不重复的学号。
8. STL 算法:sort 和 find
STL 提供了很多现成的算法函数,配合容器和迭代器使用,功能强大。
8.1 sort 排序
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> nums = {5, 2, 8, 1, 9, 3};
// 升序排序
sort(nums.begin(), nums.end());
cout << "升序: ";
for (int n : nums) {
cout << n << " ";
}
cout << endl;
// 降序排序
sort(nums.begin(), nums.end(), greater<int>());
cout << "降序: ";
for (int n : nums) {
cout << n << " ";
}
cout << endl;
return 0;
}
输出:
升序: 1 2 3 5 8 9
降序: 9 8 5 3 2 1
sort 接收两个迭代器参数,表示要排序的范围:begin() 指向第一个元素,end() 指向最后一个元素的后面。greater<int>() 表示用"大于"来比较,从而实现降序。
8.2 find 查找
cpp
#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;
int main() {
vector<int> nums = {10, 20, 30, 40, 50};
auto it = find(nums.begin(), nums.end(), 30);
if (it != nums.end()) {
cout << "找到了!下标是: " << it - nums.begin() << endl;
} else {
cout << "没找到" << endl;
}
return 0;
}
输出:
找到了!下标是: 2
find 返回一个迭代器,如果找到了就指向那个元素,没找到就返回 end()。
9. 为什么 STL 值得学?
STL 的好处可以用一句话概括:别人帮你写好了,你直接用就行。
- 高效:STL 的实现经过了大量优化,性能比你自己写的代码通常更好
- 可靠:经过了几十年的测试和验证,Bug 极少
- 通用 :学会了
vector、map、sort,你就能解决大部分编程问题 - 面试必问:STL 是 C++ 面试中的高频考点
10. 总结
这一节我们学习了 C++ 中两个非常重要的特性:
- 模板 让我们写出与类型无关的通用代码。函数模板用
template <typename T>声明,编译器会自动推导类型;类模板需要在使用时显式指定类型。 - STL 是 C++ 内置的工具箱,包含容器、算法和迭代器三大组件。
vector是自动扩容的数组,map是键值对字典,set是有序不重复集合,sort和find是最常用的算法。
模板和 STL 是 C++ 的精髓所在。掌握了它们,你就迈入了"用 C++ 高效编程"的大门。下一节,我们将继续深入探索更多 STL 容器和算法。加油!