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):存储数据的结构(如 vectorlistmap)。
  • 算法(Algorithms):对容器中数据进行操作的函数(如排序、查找)。
  • 迭代器(Iterators):连接容器与算法的桥梁,类似“数据指针”。
  • 函数对象(Functors):以函数形式表现的类对象,用于自定义算法逻辑。

比喻说明
若将 STL 比作一家图书馆,容器是书架,存放不同类型的书籍(数据);算法是图书管理员,负责整理和检索书籍;迭代器则是借书卡,帮助读者快速定位书籍位置。

1.2 STL 的优势

  • 代码复用:无需重复编写常用数据结构(如链表、哈希表)。
  • 高性能:经过高度优化的实现,例如 vector 的动态数组机制。
  • 可扩展性:支持自定义容器和算法,适应复杂需求。

二、容器:数据存储的基石

2.1 容器分类与选择

容器分为序列容器(按顺序存储)和关联容器(按键值存储)两类:

容器类型特点典型用途
vector动态数组,支持快速随机访问,插入/删除尾部高效需频繁访问元素的场景
list双向链表,插入/删除头部和中间高效,但随机访问较慢需频繁修改中间元素的场景
map键值对存储,按键排序,查找、插入、删除均高效需快速查找键值的场景
unordered_map键值对存储,哈希表实现,无序,查找效率接近常数时间高频查找且键分布均匀的场景

选择技巧
若需频繁遍历元素且访问尾部,选 vector;若需频繁插入/删除中间元素,选 list;若需通过键快速查找,选 mapunordered_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 适配器的定义与作用

适配器允许通过现有容器实现特定数据结构,例如:

  • stackqueue:基于 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 需求分析

构建一个简单的学生信息管理系统,要求:

  1. 存储学生姓名、学号和成绩。
  2. 支持按学号快速查询学生信息。
  3. 统计全班平均分。

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 的核心组件、容器选择策略、算法应用以及迭代器的使用方法。对于初学者,建议从基础容器(如 vectormap)和常用算法(如 sortfind)开始实践;中级开发者则可深入研究适配器和自定义函数对象,以应对复杂需求。

记住,STL 的精髓在于“不要重复造轮子”。通过合理利用其现成工具,您能显著提升代码质量和开发效率。接下来,尝试将本文的案例扩展为完整的项目,例如添加文件读写功能或图形界面,进一步巩固所学知识。

最新发布