STL 容器快速上手指南:从基础用法到实战代码(一)
·
STL(标准模板库)是 C++ 的核心工具集,其中容器作为数据存储的核心组件,涵盖了动态数组、链表、栈、队列等多种数据结构。掌握容器的常用方法是 STL 入门的关键,本文将结合实战代码,详细拆解 string、vector、list、stack、queue 等核心容器的常用接口,帮你快速上手 STL 开发。
一、STL 容器入门基础
1. 容器本质与核心价值
STL 容器是封装了数据结构的模板类,无需手动管理内存,提供了统一的操作接口。其核心价值在于:
- 避免重复开发底层数据结构(如动态数组、链表)。
- 接口标准化,降低学习成本(如
size()、empty()在各容器中通用)。 - 适配算法库,可直接搭配
sort()、find()等算法使用。
2. 容器分类与选择建议
| 容器类型 | 代表容器 | 核心特点 | 适用场景 |
|---|---|---|---|
| 序列式容器 | string、vector | 元素有序存储,支持下标访问 | 频繁查询、少量插入删除 |
| 链表式容器 | list | 元素双向链接,插入删除高效 | 大量插入删除、无需随机访问 |
| 容器适配器 | stack、queue | 封装基础容器,限制访问方式 | 栈(LIFO)、队列(FIFO)场景 |
| 优先队列 | priority_queue | 自动按优先级排序 | TopK 问题、任务调度 |
二、核心容器常用方法 + 实战代码
1. string 类:字符串高效管理
string 是专门处理字符串的容器,封装了 C 语言字符串的缺陷,提供了丰富的字符串操作接口。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | string()、string(const char* s) | 空构造、用 C 字符串构造 |
| string(size_t n, char c) | 构造含 n 个字符 c 的字符串 | |
| 容量操作 | size()/length() | 返回有效字符长度(功能一致) |
| capacity() | 返回总存储空间大小 | |
| empty() | 判断是否为空串(空返回 true) | |
| reserve(size_t n) | 预留 n 个字符空间(不初始化,提升效率) | |
| resize(size_t n, char c) | 调整有效字符数为 n,多余部分用 c 填充 | |
| 访问与遍历 | operator[] (pos) | 直接访问 pos 位置字符(越界风险) |
| begin()/end() | 正向迭代器,用于遍历 | |
| 范围 for 循环 | C++11 特性,简化遍历逻辑 | |
| 修改操作 | operator+= (str/char) | 追加字符串或字符(最常用) |
| push_back(char c) | 尾插单个字符 | |
| find(char c, pos) | 从 pos 开始向后找 c,返回索引(未找到返回 npos) | |
| substr(pos, n) | 从 pos 开始截取 n 个字符,返回子串 | |
| clear() | 清空有效字符(不释放底层空间) | |
| 输入输出 | operator>>/operator<< | 标准输入输出 |
| getline(cin, s) | 读取一行字符串(包括空格) |
实战代码示例
#include <iostream>
#include <string>
using namespace std;
int main() {
// 1. 构造与初始化
string s1; // 空构造
string s2("hello STL"); // C字符串构造
string s3(5, 'a'); // 5个'a':"aaaaa"
string s4(s2); // 拷贝构造
// 2. 容量操作
cout << "s2长度:" << s2.size() << endl; // 输出 9
cout << "s2容量:" << s2.capacity() << endl; // 输出 9(VS下默认不预留额外空间)
s2.reserve(20); // 预留20个字符空间
cout << "预留后容量:" << s2.capacity() << endl;// 输出 20
// 3. 访问与遍历
cout << "s2[0]:" << s2[0] << endl; // 输出 'h'
// 迭代器遍历
for (auto it = s2.begin(); it != s2.end(); ++it) {
cout << *it << " "; // 输出 h e l l o S T L
}
cout << endl;
// 范围for遍历(C++11)
for (auto ch : s2) {
cout << ch; // 输出 hello STL
}
cout << endl;
// 4. 修改操作
s2 += "!!!"; // 追加字符串:"hello STL!!!"
s2.push_back('?'); // 尾插字符:"hello STL!!!?"
cout << "修改后:" << s2 << endl; // 输出 hello STL!!!?
size_t pos = s2.find('S'); // 查找'S'的位置
string sub = s2.substr(pos, 3); // 截取从'S'开始的3个字符
cout << "子串:" << sub << endl; // 输出 STL
// 5. 输入输出
string s5;
getline(cin, s5); // 读取一行输入(如"hello world")
cout << "输入内容:" << s5 << endl; // 输出 hello world
return 0;
}
2. vector:动态数组(最常用容器)
vector 是动态增长的数组,支持随机访问,底层为连续空间,是实际开发中使用频率最高的容器。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | vector() | 空构造 |
| vector(size_t n, T val) | 构造含 n 个 val 的 vector | |
| vector(iterator first, iterator last) | 用迭代器区间构造 | |
| 容量操作 | size() | 返回有效元素个数 |
| capacity() | 返回总容量(VS 下 1.5 倍扩容,G++ 下 2 倍扩容) | |
| empty() | 判断是否为空 | |
| reserve(size_t n) | 预留 n 个元素空间(避免频繁扩容) | |
| resize(size_t n, T val) | 调整元素个数为 n,多余部分用 val 填充 | |
| 访问与遍历 | operator[] (pos) | 下标访问 pos 位置元素(高效) |
| begin()/end() | 正向迭代器 | |
| rbegin()/rend() | 反向迭代器(从尾到头遍历) | |
| 增删操作 | push_back(T val) | 尾插元素(常用) |
| pop_back() | 尾删元素(常用) | |
| insert(iterator pos, T val) | 在 pos 位置插入 val(效率低,需搬移元素) | |
| erase(iterator pos) | 删除 pos 位置元素(返回下一个有效迭代器) | |
| swap(vector& other) | 交换两个 vector 的内容 | |
| 其他操作 | clear() | 清空所有元素(不释放空间) |
实战代码示例
#include <iostream>
#include <vector>
#include <algorithm> // 包含find算法
using namespace std;
int main() {
// 1. 构造与初始化
vector<int> v1; // 空构造
vector<int> v2(5, 10); // 5个10:[10,10,10,10,10]
int arr[] = {1,2,3,4,5};
vector<int> v3(arr, arr+5); // 用数组区间构造:[1,2,3,4,5]
// 2. 容量操作
cout << "v3大小:" << v3.size() << endl; // 输出 5
cout << "v3容量:" << v3.capacity() << endl; // 输出 5(G++下初始容量=大小)
v3.reserve(10); // 预留10个空间
cout << "预留后容量:" << v3.capacity() << endl; // 输出 10
// 3. 访问与遍历
cout << "v3[2]:" << v3[2] << endl; // 输出 3(下标访问)
// 反向迭代器遍历
for (auto it = v3.rbegin(); it != v3.rend(); ++it) {
cout << *it << " "; // 输出 5 4 3 2 1
}
cout << endl;
// 4. 增删操作
v3.push_back(6); // 尾插:[1,2,3,4,5,6]
v3.pop_back(); // 尾删:[1,2,3,4,5]
// 插入(在第2个元素后插入3)
auto pos = v3.begin() + 2;
v3.insert(pos, 3); // 结果:[1,2,3,3,4,5]
// 删除(删除第一个3)
pos = find(v3.begin(), v3.end(), 3); // 查找3的位置
v3.erase(pos); // 结果:[1,2,3,4,5]
// 5. 遍历输出最终结果
for (auto e : v3) {
cout << e << " "; // 输出 1 2 3 4 5
}
cout << endl;
return 0;
}
关键注意点
- 迭代器失效:
insert()、erase()、reserve()等操作可能导致迭代器失效,需重新赋值迭代器(如pos = v3.erase(pos))。 - 扩容机制:VS 下按 1.5 倍扩容,G++ 下按 2 倍扩容,扩容时会拷贝旧元素到新空间,建议用
reserve()提前预留空间。
3. list 类:双向循环链表
list 底层是带头结点的双向循环链表,插入删除操作效率极高(无需搬移元素),但不支持随机访问。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | list()、list(size_t n, T val) | 空构造、构造含 n 个 val 的链表 |
| 容量操作 | size() | 返回有效节点个数 |
| empty() | 判断是否为空 | |
| 访问操作 | front() | 返回第一个元素的引用 |
| back() | 返回最后一个元素的引用 | |
| 增删操作 | push_front(T val) | 头插元素(高效) |
| push_back(T val) | 尾插元素(高效) | |
| pop_front() | 头删元素(高效) | |
| pop_back() | 尾删元素(高效) | |
| insert(iterator pos, T val) | 在 pos 位置插入 val(O (1) 效率) | |
| erase(iterator pos) | 删除 pos 位置元素(返回下一个迭代器) | |
| 其他操作 | clear() | 清空所有元素 |
| swap(list& other) | 交换两个链表内容 |
实战代码示例
#include <iostream>
#include <list>
using namespace std;
int main() {
// 1. 构造与初始化
list<int> l1; // 空构造
list<int> l2(4, 20); // 4个20:[20,20,20,20]
int arr[] = {10,20,30,40};
list<int> l3(arr, arr+4); // 用数组构造:[10,20,30,40]
// 2. 访问操作
cout << "首元素:" << l3.front() << endl; // 输出 10
cout << "尾元素:" << l3.back() << endl; // 输出 40
// 3. 增删操作(高效)
l3.push_front(5); // 头插:[5,10,20,30,40]
l3.push_back(45); // 尾插:[5,10,20,30,40,45]
l3.pop_front(); // 头删:[10,20,30,40,45]
l3.pop_back(); // 尾删:[10,20,30,40]
// 插入和删除中间元素
auto pos = l3.begin();
++pos; // 指向第二个元素20
l3.insert(pos, 15); // 插入15:[10,15,20,30,40]
pos = l3.begin() + 3; // 指向30(注意:list不支持随机访问,需迭代器移动)
l3.erase(pos); // 删除30:[10,15,20,40]
// 4. 遍历输出
for (auto e : l3) {
cout << e << " "; // 输出 10 15 20 40
}
cout << endl;
return 0;
}
关键注意点
- 不支持随机访问:不能用
l3[2]访问元素,需通过迭代器移动(如++pos)。 - 迭代器失效:仅删除元素时,被删除节点的迭代器失效,其他迭代器不受影响(与 vector 不同)。
4. stack 类:栈(容器适配器)
stack 是基于其他容器(默认 deque)封装的适配器,遵循 “后进先出(LIFO)” 原则,仅支持栈顶操作。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | stack() | 空构造 |
| 容量操作 | empty() | 判断栈是否为空(空返回 true) |
| size() | 返回栈中元素个数 | |
| 核心操作 | push(T val) | 栈顶压入元素 |
| pop() | 栈顶弹出元素(无返回值) | |
| top() | 返回栈顶元素的引用(不弹出) |
实战代码示例:最小栈实现
#include <iostream>
#include <stack>
using namespace std;
// 设计一个支持getMin()的最小栈
class MinStack {
public:
void push(int x) {
_elem.push(x); // 元素入栈
// 最小栈为空或x小于等于栈顶,入栈
if (_min.empty() || x <= _min.top()) {
_min.push(x);
}
}
void pop() {
// 若弹出的是最小值,最小栈也弹出
if (_elem.top() == _min.top()) {
_min.pop();
}
_elem.pop();
}
int top() {
return _elem.top(); // 返回栈顶元素
}
int getMin() {
return _min.top(); // 返回最小值
}
private:
stack<int> _elem; // 存储所有元素
stack<int> _min; // 存储最小值(辅助栈)
};
int main() {
MinStack ms;
ms.push(3);
ms.push(2);
ms.push(5);
ms.push(1);
cout << "栈顶元素:" << ms.top() << endl; // 输出 1
cout << "当前最小值:" << ms.getMin() << endl;// 输出 1
ms.pop(); // 弹出1(最小值)
cout << "弹出后最小值:" << ms.getMin() << endl;// 输出 2
return 0;
}
5. queue 类:队列(容器适配器)
queue 基于 deque 或 list 封装,遵循 “先进先出(FIFO)” 原则,支持队尾入队、队头出队。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | queue() | 空构造 |
| 容量操作 | empty() | 判断队列是否为空 |
| size() | 返回队列元素个数 | |
| 核心操作 | push(T val) | 队尾入队 |
| pop() | 队头出队(无返回值) | |
| front() | 返回队头元素引用 | |
| back() | 返回队尾元素引用 |
实战代码示例:队列模拟
#include <iostream>
#include <queue>
using namespace std;
int main() {
queue<int> q;
// 入队操作
q.push(10);
q.push(20);
q.push(30);
cout << "队列大小:" << q.size() << endl; // 输出 3
cout << "队头元素:" << q.front() << endl; // 输出 10
cout << "队尾元素:" << q.back() << endl; // 输出 30
// 出队操作
q.pop(); // 弹出10
cout << "出队后队头:" << q.front() << endl; // 输出 20
// 遍历队列(需出队操作)
while (!q.empty()) {
cout << q.front() << " "; // 输出 20 30
q.pop();
}
cout << endl;
return 0;
}
6. priority_queue:优先队列
priority_queue 默认基于 vector 封装,底层是大根堆,元素自动按优先级排序(默认降序)。
常用方法速查表
| 方法分类 | 常用方法 | 功能说明 |
|---|---|---|
| 构造与初始化 | priority_queue() | 空构造(大根堆) |
| priority_queue(first, last) | 用迭代器区间构造 | |
| priority_queue(vector<T>, greater<T>) | 构造小根堆 | |
| 容量操作 | empty()、size() | 判断为空、返回元素个数(同 stack) |
| 核心操作 | push(T val) | 入队(自动调整堆结构) |
| pop() | 出队(删除堆顶元素) | |
| top() | 返回堆顶元素(最大值 / 最小值) |
实战代码示例:TopK 问题
#include <iostream>
#include <vector>
#include <queue>
using namespace std;
// 找出数组中第K大的元素
int findKthLargest(vector<int>& nums, int k) {
// 构造小根堆(容量为k)
priority_queue<int, vector<int>, greater<int>> pq;
for (int num : nums) {
pq.push(num);
// 堆大小超过k,弹出最小值(保持堆顶为第k大)
if (pq.size() > k) {
pq.pop();
}
}
return pq.top(); // 堆顶即为第k大元素
}
int main() {
vector<int> nums = {3,2,1,5,6,4};
int k = 2;
cout << "第" << k << "大的元素:" << findKthLargest(nums, k) << endl; // 输出 5
return 0;
}
三、容器使用避坑指南
- 迭代器失效处理:
- vector:
insert()、erase()、reserve()后,迭代器需重新赋值(如it = v.erase(it))。 - list:仅删除节点的迭代器失效,其他迭代器可正常使用。
- vector:
- 内存效率优化:
- string/vector:提前用
reserve()预留空间,减少扩容次数。 - list:避免频繁创建小节点(易造成内存碎片)。
- string/vector:提前用
- 接口使用误区:
- string 的
c_str()返回的指针有效期与 string 对象一致,不可长期保存。 - stack/queue 无迭代器,不可遍历(需通过出队 / 出栈操作间接遍历)。
- string 的
更多推荐
所有评论(0)