C++ STL 教程(千字长文)
💡一则或许对你有用的小广告
欢迎加入小哈的星球 ,你将获得:专属的项目实战 / 1v1 提问 / Java 学习路线 / 学习打卡 / 每月赠书 / 社群讨论
- 新项目:《从零手撸:仿小红书(微服务架构)》 正在持续爆肝中,基于
Spring Cloud Alibaba + Spring Boot 3.x + JDK 17...
,点击查看项目介绍 ;- 《从零手撸:前后端分离博客项目(全栈开发)》 2 期已完结,演示链接: http://116.62.199.48/ ;
截止目前, 星球 内专栏累计输出 82w+ 字,讲解图 3441+ 张,还在持续爆肝中.. 后续还会上新更多项目,目标是将 Java 领域典型的项目都整一波,如秒杀系统, 在线商城, IM 即时通讯,权限管理,Spring Cloud Alibaba 微服务等等,已有 2900+ 小伙伴加入学习 ,欢迎点击围观
前言:为什么学习 C++ STL?
在编程世界中,标准模板库(Standard Template Library,STL)是 C++ 的核心组成部分。它提供了一系列高效且功能强大的工具,帮助开发者快速实现复杂的数据结构和算法。对于编程初学者而言,掌握 STL 能显著提升代码效率和可读性;对于中级开发者,深入理解 STL 的设计原理则有助于优化代码性能。本文将通过循序渐进的方式,结合实际案例,带您系统学习 C++ STL 的关键知识点。
一、STL 的核心概念与结构
1.1 什么是 STL?
STL 是 C++ 标准库中的一部分,它由四个主要组件构成:
- 容器(Containers):存储数据的结构(如
vector
、list
、map
)。 - 算法(Algorithms):对容器中数据进行操作的函数(如排序、查找)。
- 迭代器(Iterators):连接容器与算法的桥梁,类似“数据指针”。
- 函数对象(Functors):以函数形式表现的类对象,用于自定义算法逻辑。
比喻说明:
若将 STL 比作一家图书馆,容器是书架,存放不同类型的书籍(数据);算法是图书管理员,负责整理和检索书籍;迭代器则是借书卡,帮助读者快速定位书籍位置。
1.2 STL 的优势
- 代码复用:无需重复编写常用数据结构(如链表、哈希表)。
- 高性能:经过高度优化的实现,例如
vector
的动态数组机制。 - 可扩展性:支持自定义容器和算法,适应复杂需求。
二、容器:数据存储的基石
2.1 容器分类与选择
容器分为序列容器(按顺序存储)和关联容器(按键值存储)两类:
容器类型 | 特点 | 典型用途 |
---|---|---|
vector | 动态数组,支持快速随机访问,插入/删除尾部高效 | 需频繁访问元素的场景 |
list | 双向链表,插入/删除头部和中间高效,但随机访问较慢 | 需频繁修改中间元素的场景 |
map | 键值对存储,按键排序,查找、插入、删除均高效 | 需快速查找键值的场景 |
unordered_map | 键值对存储,哈希表实现,无序,查找效率接近常数时间 | 高频查找且键分布均匀的场景 |
选择技巧:
若需频繁遍历元素且访问尾部,选 vector
;若需频繁插入/删除中间元素,选 list
;若需通过键快速查找,选 map
或 unordered_map
。
2.2 典型容器的使用示例
示例 1:vector
的基本操作
#include <vector>
#include <iostream>
int main() {
// 创建一个整数向量
std::vector<int> numbers = {1, 2, 3, 4};
// 添加元素
numbers.push_back(5); // 尾部添加
// 随机访问
std::cout << "第三个元素: " << numbers[2] << std::endl; // 输出 3
// 遍历容器
for (const auto& num : numbers) {
std::cout << num << " ";
}
// 输出: 1 2 3 4 5
return 0;
}
示例 2:map
的键值对操作
#include <map>
#include <string>
int main() {
std::map<std::string, int> student_scores = {
{"Alice", 90}, {"Bob", 85}, {"Charlie", 95}
};
// 查找元素
auto it = student_scores.find("Bob");
if (it != student_scores.end()) {
std::cout << "Bob's score: " << it->second << std::endl; // 输出 85
}
// 修改值
student_scores["Alice"] = 92; // 更新 Alice 的分数
return 0;
}
三、算法:数据操作的瑞士军刀
3.1 算法的核心功能
STL 算法提供了一套通用操作,例如:
std::sort
:对容器进行排序。std::find
:在容器中查找特定值。std::for_each
:对容器的每个元素执行操作。
3.2 算法与迭代器的关系
算法通过迭代器访问容器数据,例如:
#include <algorithm>
#include <vector>
int main() {
std::vector<int> vec = {5, 3, 8, 1, 2};
// 使用 std::sort 排序
std::sort(vec.begin(), vec.end()); // 排序后 vec 为 {1, 2, 3, 5, 8}
// 使用 std::find 查找元素
auto found = std::find(vec.begin(), vec.end(), 8);
if (found != vec.end()) {
std::cout << "找到元素 8" << std::endl;
}
return 0;
}
3.3 自定义算法逻辑
通过函数对象或Lambda 表达式扩展算法功能:
#include <algorithm>
#include <vector>
// 自定义函数对象
struct GreaterThanFive {
bool operator()(int num) const {
return num > 5;
}
};
int main() {
std::vector<int> nums = {3, 6, 2, 9, 4};
// 使用 std::count_if 统计大于5的元素数量
int count = std::count_if(nums.begin(), nums.end(), GreaterThanFive());
std::cout << "数量: " << count << std::endl; // 输出 2
// 使用 Lambda 表达式简化
count = std::count_if(nums.begin(), nums.end(), [](int x) { return x > 5; });
std::cout << "数量: " << count << std::endl; // 同样输出 2
return 0;
}
四、迭代器:连接容器与算法的纽带
4.1 迭代器的类型与作用
迭代器是访问容器元素的通用接口,常见的类型包括:
- 输入迭代器(Input Iterator):单向读取。
- 输出迭代器(Output Iterator):单向写入。
- 前向迭代器(Forward Iterator):支持多次遍历。
- 双向迭代器(Bidirectional Iterator):支持前后移动(如
list
)。 - 随机访问迭代器(Random Access Iterator):支持直接访问(如
vector
)。
4.2 迭代器的使用示例
#include <vector>
#include <iostream>
int main() {
std::vector<int> vec = {10, 20, 30};
// 使用迭代器遍历
for (auto it = vec.begin(); it != vec.end(); ++it) {
std::cout << *it << " "; // 输出 10 20 30
}
// 反向迭代器示例
for (auto rit = vec.rbegin(); rit != vec.rend(); ++rit) {
std::cout << *rit << " "; // 输出 30 20 10
}
return 0;
}
五、适配器:扩展容器功能
5.1 适配器的定义与作用
适配器允许通过现有容器实现特定数据结构,例如:
stack
和queue
:基于deque
实现后进先出和先进先出。priority_queue
:基于堆实现优先级队列。
5.2 示例:优先队列的使用
#include <queue>
#include <vector>
#include <iostream>
int main() {
std::priority_queue<int> pq;
pq.push(3);
pq.push(1);
pq.push(4);
while (!pq.empty()) {
std::cout << pq.top() << " "; // 输出 4 3 1(默认最大堆)
pq.pop();
}
return 0;
}
六、实践案例:学生信息管理系统
6.1 需求分析
构建一个简单的学生信息管理系统,要求:
- 存储学生姓名、学号和成绩。
- 支持按学号快速查询学生信息。
- 统计全班平均分。
6.2 实现代码
#include <iostream>
#include <map>
#include <vector>
#include <numeric> // for std::accumulate
struct Student {
std::string name;
int id;
double score;
};
// 自定义比较函数,按学号排序
bool compare_by_id(const Student& a, const Student& b) {
return a.id < b.id;
}
int main() {
// 使用 map 存储学生信息(学号为键)
std::map<int, Student> students;
// 添加学生数据
students[1001] = {"Alice", 1001, 90.5};
students[1002] = {"Bob", 1002, 85.0};
students[1003] = {"Charlie", 1003, 92.0};
// 查询学生 Bob 的成绩
auto search = students.find(1002);
if (search != students.end()) {
std::cout << "Bob 的成绩: " << search->second.score << std::endl;
}
// 统计平均分
double total = 0.0;
for (const auto& pair : students) {
total += pair.second.score;
}
double average = total / students.size();
std::cout << "班级平均分: " << average << std::endl; // 输出 (90.5+85+92)/3 ≈ 89.17
return 0;
}
结论:掌握 STL 的关键路径
通过本文的学习,您已了解 C++ STL 的核心组件、容器选择策略、算法应用以及迭代器的使用方法。对于初学者,建议从基础容器(如 vector
、map
)和常用算法(如 sort
、find
)开始实践;中级开发者则可深入研究适配器和自定义函数对象,以应对复杂需求。
记住,STL 的精髓在于“不要重复造轮子”。通过合理利用其现成工具,您能显著提升代码质量和开发效率。接下来,尝试将本文的案例扩展为完整的项目,例如添加文件读写功能或图形界面,进一步巩固所学知识。