C++ 标准模板库STL(Standard Template Library)

"STL 是 C++ 标准模板库,包含容器、算法、迭代器、函数对象、适配器、分配器六大组件。它采用泛型编程思想,通过迭代器将算法与容器解耦,提供高效、通用的数据结构和算法实现,是 C++ 编程的基础工具集。"

1. STL 的六大组件

STL 包含六大核心组件,用这个记忆法:AC2FIM

复制代码
// 六大组件关系
STL = Algorithms (算法) + Containers (容器) + Iterators (迭代器)
     + Function objects (函数对象) + Adapters (适配器) + Allocators (分配器)

这个缩写对应 STL 最核心的六个部分:

字母 对应组件 (英文) 中文含义 核心作用
A Algorithms 算法 提供 sortfind等通用算法
C Containers 容器 提供 vectormap等数据结构
2 Iterators 迭代器 充当容器与算法之间的"胶水"
F Functors 函数对象/仿函数 让对象像函数一样被调用
I Adapters 适配器 接口转换(如 stackdeque适配而来)
M Allocators 分配器 管理内存的分配与释放

:其中的 2 ​ 是取 It erators 的谐音("I" 和 "t" 连读类似 "two"),I ​ 是取 Adapters 中的 'd' 音变而来。这是一种常见的记忆技巧。


2. 容器(Containers)

容器是存储数据的模板类,分为三类:

① 序列容器(Sequence Containers)
cpp 复制代码
#include <vector>    // 动态数组
#include <deque>     // 双端队列
#include <list>      // 双向链表
#include <forward_list>  // 单向链表(C++11)
#include <array>     // 固定大小数组(C++11)

// 示例
std::vector<int> vec = {1, 2, 3};  // 随机访问,尾部插入高效
std::list<int> lst = {1, 2, 3};    // 任意位置插入高效
std::array<int, 3> arr = {1, 2, 3}; // 编译时固定大小

"插入高效" 指在特定位置插入元素的时间复杂度低

② 关联容器(Associative Containers)
复制代码
#include <set>       // 集合(值唯一,自动排序)
#include <map>       // 映射(键值对,键唯一,自动排序)
#include <multiset>  // 多重集合(值可重复)
#include <multimap>  // 多重映射(键可重复)

// 示例
std::set<int> s = {3, 1, 4, 1, 5};  // {1, 3, 4, 5}
std::map<std::string, int> m = {{"Alice", 25}, {"Bob", 30}};

"关联" 指的是元素之间通过键(key)建立逻辑关系

③ 无序容器(Unordered Containers,C++11)
复制代码
#include <unordered_set>      // 哈希集合
#include <unordered_map>      // 哈希映射
#include <unordered_multiset> // 哈希多重集合
#include <unordered_multimap> // 哈希多重映射

// 示例
std::unordered_map<std::string, int> um = {{"Alice", 25}, {"Bob", 30}};
// 基于哈希,访问O(1),但无序

哈希是一种将任意长度数据映射为固定长度值的计算过程 ,核心思想是通过哈希函数 建立键 → 桶索引的快速映射,实现近似O(1)的查找速度。

桶是哈希表中的基本存储单元 ,可以看作数组的一个槽位 ,每个桶存放哈希值相同的元素(解决冲突时可能存放多个)。哈希值不直接匹配,而是通过取模运算映射到桶索引 。具体过程是:哈希值 → 桶索引 → 桶内线性比较

// 把哈希表想象成一个有很多格子的储物柜

哈希表 = 储物柜

├── 桶0 = 1号柜子 [ ]

├── 桶1 = 2号柜子 [("Alice", 25), ("Bob", 30)] ← 这个柜子有2个物品

├── 桶2 = 3号柜子 [ ]

├── 桶3 = 4号柜子 [("Charlie", 35)]

└── ...

// 哈希函数决定物品放哪个柜子

// 冲突:不同物品哈希到同一柜子 → 都放进去

cpp 复制代码
// 查找键 "Alice" 的过程
std::unordered_map<std::string, int> um = {{"Alice", 25}, {"Bob", 30}};

// 1. 计算哈希值
size_t hash_value = std::hash<std::string>{}("Alice");
// 假设得到: 0x5A3F8C1B (十进制: 1513473051)

// 2. 映射到桶索引
size_t bucket_index = hash_value % um.bucket_count();
// 假设有8个桶: 1513473051 % 8 = 3
// 所以应该去桶3找

// 3. 在桶3内线性比较
// 遍历桶3的链表,用 operator== 逐个比较键
// 找到 "Alice" == "Alice",返回对应的值25

3. 算法(Algorithms)

<algorithm>头文件包含 100+ 个通用算法,不依赖于具体容器:

复制代码
#include <algorithm>  // 最重要的头文件

std::vector<int> v = {3, 1, 4, 1, 5, 9, 2, 6};

// 常用算法
std::sort(v.begin(), v.end());           // 排序
auto it = std::find(v.begin(), v.end(), 5);  // 查找
int count = std::count(v.begin(), v.end(), 1);  // 计数
std::reverse(v.begin(), v.end());        // 反转
std::rotate(v.begin(), v.begin() + 3, v.end());  // 旋转
bool sorted = std::is_sorted(v.begin(), v.end());  // 检查是否有序

// 数值算法
#include <numeric>
int sum = std::accumulate(v.begin(), v.end(), 0);  // 求和

算法分类

  • 不修改序列:find, count, search, equal

  • 修改序列:copy, transform, replace, fill

  • 排序相关:sort, stable_sort, nth_element, partition

  • 数值运算:accumulate, inner_product, partial_sum


4. 迭代器(Iterators)

迭代器是连接容器和算法的桥梁,提供统一的访问接口,用于遍历和访问容器中的元素

!算法通过迭代器操作容器

迭代器是:智能下标

从用法上看:迭代器完全可以看作访问容器元素的下标,作用一模一样;
从本质上看:它不是整数下标,而是指向元素的指针对象;
从能力上看:它是所有容器通用的下标,数组下标做不到这一点

复制代码
// 五种迭代器类别
1. 输入迭代器 (Input Iterator)     : 只读,单次遍历
2. 输出迭代器 (Output Iterator)    : 只写,单次遍历
3. 前向迭代器 (Forward Iterator)   : 可读写,可重复遍历
4. 双向迭代器 (Bidirectional Iterator): 可向前向后移动
5. 随机访问迭代器 (Random Access Iterator): 支持随机访问

// 示例
std::vector<int> vec = {1, 2, 3, 4, 5};
for (auto it = vec.begin(); it != vec.end(); ++it) {
    std::cout << *it << " ";  // 通过迭代器访问
}
// 等价于范围for
for (int val : vec) {
    std::cout << val << " ";
}

5. 函数对象(Function Objects)和 Lambda

任何重载了 operator()的 struct/class 对象,都能像函数一样被调用,因此称为函数对象(仿函数)

operator()是函数调用运算符,是 C++ 允许类/结构体重载的特殊运算符,让对象能像函数一样被调用。

定义

cpp 复制代码
返回类型 operator()(参数列表) [const] [异常说明] [-> 尾置返回类型] {
    函数体
}

和成员函数的使用相对比,成员函数要加个点,调用

cpp 复制代码
class Calculator {
public:
    // 普通成员函数
    int add(int a, int b) { return a + b; }
    
    // 函数调用运算符
    int operator()(int a, int b) { return a + b; }
};

Calculator calc;

// 使用普通成员函数
int r1 = calc.add(3, 4);  // calc.add(3, 4)

// 使用 operator()
int r2 = calc(3, 4);      // calc.operator()(3, 4)
                          // 语法更简洁,像调用函数
cpp 复制代码
#include <functional>

// 1. 函数对象(仿函数)
struct Add {
    int operator()(int a, int b) const { return a + b; }
};
Add add;
int result = add(3, 4);  // 7

---------------------------------------------------------------------------
cpp 复制代码
#include <functional>  // 需要这个头文件

// STL 预定义的函数对象模板
std::plus<int> p;        // 加法:p(a, b) 返回 a + b
std::less<int> l;        // 小于比较:l(a, b) 返回 a < b
std::logical_and<bool> la;  // 逻辑与:la(a, b) 返回 a && b

"预定义" 指的是 C++ 标准库已经提前定义好的模板类,我们可以直接使用,不需要自己实现。

函数适配器已弃用。!=适配器

6. 适配器(Adapters)

适配器修改现有组件的接口:

  • 容器适配器:适配底层容器的接口

  • 迭代器适配器:适配迭代器的行为

  • 函数适配器:适配函数的调用方式

接口

(Interface)是类/对象对外提供的操作方法的集合 ,定义了如何使用 这个对象,但不暴露如何实现

生活中的接口:
复制代码
// 比如电视机遥控器:
遥控器接口 = {开关(), 调音量(), 换台(), 静音()}
// 你只需要知道按哪个按钮,不需要知道电视内部电路
编程中的接口
复制代码
// 比如 vector 的接口:
vector接口 = {
    push_back(),   // 添加元素
    pop_back(),    // 移除元素
    at(),          // 访问元素
    size(),        // 获取大小
    empty(),       // 是否为空
    // ... 其他操作
}
// 你只需要调用这些方法,不需要知道 vector 内部如何存储

7. 分配器(Allocators)

管理内存分配,通常使用默认分配器:

复制代码
template<class T>
class MyAllocator {
    // 自定义内存分配策略
};

std::vector<int, MyAllocator<int>> v;  // 使用自定义分配器

8. STL 的设计哲学

泛型编程是一种编程范式

STL 是 C++ 中对泛型编程思想的具体实现

Template 是泛型编程的实现工具

泛型编程是一种编程思想

思想:编写不依赖于具体类型的代码

目标:提高代码复用性、类型安全性

原则:算法与数据结构解耦,"解耦" 指算法不直接依赖具体数据结构,而是通过统一接口(迭代器)间接访问数据。

cpp 复制代码
// 定义泛型查找算法
template<typename InputIt, typename T>  // 模板:InputIt=迭代器类型,T=值类型
InputIt my_find(InputIt first, InputIt last, const T& value) {
    // 遍历范围 [first, last)
    for (; first != last; ++first) {    // 迭代器前进:++first
        if (*first == value)            // 解引用迭代器:*first
            return first;               // 找到,返回迭代器
    }
    return last;  // 没找到,返回 last
}
复制代码
// 同一算法适用于不同容器
template<typename Container>
void print_container(const Container& c) {
    for (const auto& item : c) {
        std::cout << item << " ";
    }
}

std::vector<int> v = {1, 2, 3};
std::list<int> l = {4, 5, 6};
print_container(v);  // 同一函数处理不同容器
print_container(l);
迭代器解耦
复制代码
// 算法不依赖具体容器
template<typename InputIt, typename T>
InputIt my_find(InputIt first, InputIt last, const T& value) {
    for (; first != last; ++first) {
        if (*first == value) return first;
    }
    return last;
}

// 可用于vector、list、array等
std::vector<int> vec = {1, 2, 3};
std::list<int> lst = {1, 2, 3};
auto it1 = my_find(vec.begin(), vec.end(), 2);
auto it2 = my_find(lst.begin(), lst.end(), 2);

9. 现代 C++ 中的 STL 发展

C++11 增强
cpp 复制代码
// 1. 移动语义
std::vector<std::string> v;
v.push_back(std::string("Hello"));  // 移动而非拷贝

// 2. 初始化列表
std::vector<int> v = {1, 2, 3, 4, 5};  // 列表初始化

// 3. auto 类型推导
for (auto it = v.begin(); it != v.end(); ++it) {}

// 4. 基于范围的for循环
for (int x : v) { std::cout << x; }
  • push_back通过移动而非拷贝将其放入 vector,避免不必要的深拷贝

  • 栈上创建 temp对象

  • push_back在堆上分配新内存

  • 复制 "Hello"的内容

  • 复制后,有两个 "Hello"字符串

  • 函数结束,temp被销毁


10. 常用 STL 组件总结

类别 常用组件 描述
容器 vector, list, deque, array 序列容器
set, map, multiset, multimap 关联容器
unordered_set, unordered_map 无序容器
适配器 stack, queue, priority_queue 容器适配器
算法 sort, find, transform, copy 通用算法
迭代器 begin(), end(), rbegin(), rend() 访问元素
函数对象 plus, less, equal_to 预定义仿函数
智能指针 unique_ptr, shared_ptr, weak_ptr 内存管理

11. STL常见问答

Q1:vector 和 list 的区别?

A

复制代码
vector: 连续内存,随机访问O(1),中间插入O(n)
list:   不连续内存,随机访问O(n),中间插入O(1)
选择:需要快速访问用vector,需要频繁插入用list
Q2:map 和 unordered_map 的区别?

A

复制代码
map:           红黑树实现,有序,O(log n)访问
unordered_map: 哈希表实现,无序,平均O(1)访问
选择:需要有序遍历用map,需要快速查找用unordered_map
Q3:迭代器失效问题?

A

复制代码
vector: 插入/删除可能使所有迭代器失效
list:   插入不会失效,删除只使被删元素迭代器失效
map:    插入不会失效,删除只使被删元素迭代器失效
Q4:STL 算法的时间复杂度?

A

复制代码
sort: O(n log n)
find: O(n)  // 线性查找
binary_search: O(log n)  // 有序序列
count: O(n)
相关推荐
我真不是小鱼2 小时前
cpp刷题打卡记录27——无重复字符的最长子串 & 找到字符串中所有字母的异位词
数据结构·c++·算法·leetcode
一直不明飞行2 小时前
C++:string,写法s.find(‘@‘) != s.end()是否有问题
开发语言·c++·算法
无限进步_2 小时前
【C++】重载、重写和重定义的区别详解
c语言·开发语言·c++·ide·windows·git·github
历程里程碑2 小时前
1 . Git本地操作:版本控制 跨平台协作 仓库核心
java·开发语言·数据结构·c++·git·gitee·github
小欣加油2 小时前
leetcode 42 接雨水
c++·算法·leetcode·职场和发展
ZXF_H3 小时前
VSCode C/C++函数Ctrl+鼠标点击无法跳转的解决方法
c++·ide·vscode
tankeven3 小时前
动态规划专题(14):石子合并问题(未完待续)
c++·算法·动态规划
cpp_25013 小时前
P1910 L 国的战斗之间谍
数据结构·c++·算法·题解·洛谷·背包dp
txzrxz3 小时前
c++深度搜索讲解及例题
开发语言·c++·深度搜索·例题讲解