C++STL的学习与使用
背景:
随着开发的深入,使用链表,队列等数据结构是必须要掌握的技能,但是比起手动实现这些数据结构,C++提供的STL容器封装了底层细节,且对接口进行了统一,大大提高了代码的简洁性和可维护性。同时,STL自动处理内存分配和释放,避免了手动实现可能导致的内存泄漏或碎片化,提高了代码的安全性。
STL的设计遵循泛型编程理念,通过模板实现通用性,同一容器或算法可处理任意数据类型,增强代码复用性。
一、vector
:动态数组管理
注:在学习C++时候,我们已经知道可以用下标运算符访问string和vector对象的字符或者vector对象的元素,但是还有另一种机制可以帮助我们同样完成访问,这就是迭代器。
类似于指针类型,迭代器也可以间接的访问对象,使用迭代器访问对象中的元素,也就是迭代器从一个元素移动到另一个元素中去。迭代器分为有效和无效,和指针一样,有效的迭代器指向具体的某个元素或者迭代器指向容器中最后一个元素的下一个位置,其余为无效,也就是指向的是无意义位置或者越界访问,类似于野指针。迭代器为iterator。
原文链接
函数介绍:
#include <vector>
/*vector 生成*/
vector<int> vct(3); //0 0 0
vector<int> vct(3, 5); //5 5 5
vector<int> vct{1, 2, 3}; //1 2 3
/*vector 头尾*/
vct.front(); //第一个元素
vct.back(); //最后一个元素
/*vector 迭代器*/
vector<int>::iterator iter //一个vector的迭代器
vct.begin() //指向vct第一个元素位置的迭代器
vct.end() //指向vct最后一个元素后一个位置的迭代器
/*vector 插入*/
vct.push_back(2); //在尾部插入一个2
vct.insert(vct.begin(), 2); //在指定位置前面插入一个元素
/*vector 删除*/
vct.pop_back();
vct.erase(vct.begin());
vct.erase(vct.begin()+1, vct.end()-2);
vct.erase(1, 6);
/*vector 容量*/
vct.size()
/*vector 遍历*/
for(int i = 0; i < vct.size(); i ++ ) //索引遍历
for(vector<int>::iterator iter = vct.begin(); iter != vct.end(); iter ++ ) //迭代器遍历
for(auto &x : vct) //迭代器另一种便捷方法
/*vector 排序*/
sort(vct.begin(), vct.end());
/*vctor 查找*/
vct.find(2) //从前往后找,若找到,返回指向该处的迭代器;反之,返回迭代器vct.end()
/*vctor 某元素个数*/
vct.count(2); //返回容器里2的个数
/*vector 判空*/
vct.empty() //返回布尔值
/*vector 清空*/
vct.clear();
场景:需要动态调整大小的连续存储结构,适合频繁尾部操作。
代码案例:
#include <vector>
#include <iostream>
int main() {
// 初始化空向量
std::vector<int> scores;
// 添加学生成绩(尾部插入)
scores.push_back(85);
scores.emplace_back(92); // 更高效的插入方式
scores.insert(scores.begin(), 78); // 头部插入
// 使用迭代器遍历输出
// 常规迭代器的创建vector<int>::iterator it=v1.begin();
std::cout << "当前成绩:";
for (auto it = scores.begin(); it != scores.end(); ++it) {
std::cout << *it << " ";
}
// 与迭代器一起使用: 在使用标准库容器时,auto 可以自动获取正确的迭代器类型。
// 删除最后一个无效成绩
if (!scores.empty()) scores.pop_back();
// 范围for循环(C++11特性)
std::cout << "\n最终成绩:";
for (int s : scores) {
std::cout << s << " ";
}
return 0;
}
二、list
:双向链表
函数介绍:
#include <list>
list<int> lst;
// 插入
lst.push_back(2); // 尾部插入(类似 vector)
lst.push_front(1); // 头部插入(双向链表特有)
lst.emplace_back(3); // 原位构造尾部元素
lst.insert(++lst.begin(), 5); // 指定位置插入
// 删除
lst.pop_back(); // 尾部删除
lst.pop_front(); // 头部删除(类似队列操作)
lst.erase(lst.begin()); // 删除指定位置元素
// 其他
lst.merge(other_lst); // 合并两个链表(有序)
lst.sort(); // 链表内排序(非通用算法)
代码案例:
#include <list>
#include <algorithm>
int main() {
std::list<std::string> todos {"买菜", "写作业"};
// 在第二个位置插入任务
auto it = todos.begin();
std::advance(it, 1);
todos.insert(it, "打电话给家人");
// 使用反向迭代器输出最新任务
std::cout << "待办事项(最新优先):\n";
for (auto rit = todos.rbegin(); rit != todos.rend(); ++rit) {
std::cout << "- " << *rit << "\n";
}
// 删除特定任务
todos.remove("写作业");
return 0;
}
注:C++11 引入了许多新特性,其中包括对 STL(Standard Template Library)的改进。在 STL 容器中,rbegin() 和 rend() 是两个新的成员函数,它们分别返回指向容器最后一个元素的反向迭代器(reverse iterator)和指向容器“理论上的前一个元素”的反向迭代器(该迭代器实际上并不指向任何有效元素,而是作为结束标记)。原文链接
三、map
:有序键值对
函数介绍:
#include <map>
/*map 生成*/
map<key_type, value_type> name;
map<int, int> mp;
/*map 迭代器*/
map<int, int>::iterator iter
mp.begin()
mp.end()
/*map 键值*/
iter->first //key
iter->second //value
/*map 插入*/
mp[2] = 5; //直接添加
mp.insert(pair<int, int>(2, 5)); //insert一个pair
/*删除*/
mp.erase(iter); //删除迭代器所指的键值对
/*map 容量*/
mp.size()
/*map 查找*/
mp.find(2) //从前往后找,若找到,返回指向该处的迭代器;反之,返回迭代器mp.end()
/*map 某元素个数*/
st.count(2); //返回key为2的个数(map中只可能是0或者1)
/*map 判空*/
mp.empty() //返回布尔值
/*map 清空*/
mp.clear();
pair使用
场景:统计字符频率或存储学生成绩(键唯一且需排序)。
代码案例:
#include <map>
#include <string>
int main() {
std::map<std::string, int> word_count;
std::string text[] = {"apple", "banana", "apple", "orange"};
// 统计词频
for (const auto& word : text) {
++word_count[word]; // 自动插入新键
}
// 等价于word_count.insert(std::make_pair(word, 0)).first->second++;
// 遍历输出结果(自动按键排序)
std::cout << "词频统计:\n";
for (auto it = word_count.begin(); it != word_count.end(); ++it) {
std::cout << it->first << ": " << it->second << "次\n";
}
return 0;
}
四、unordered_map
:哈希表键值对
函数介绍:
std::unordered_map<std::string, int> scores;
// 插入键值对,若键已存在则不覆盖
scores.insert({"Alice", 95}); // 插入成功
scores.insert({"Alice", 90}); // 插入失败(键已存在)
// 原地构造键值对,减少拷贝开销
scores.emplace("Bob", 88); // 直接构造元素
// 若键不存在则插入默认值,存在则修改值
scores["Alice"] = 90; // 覆盖原有值 95
scores["Charlie"] = 92; // 插入新键值对
// 删除指定键的元素,返回删除数量(0 或 1)
scores.erase("Bob"); // 返回 1
auto it = scores.find("Alice");
if (it != scores.end()) scores.erase(it);
scores.clear(); // 容器变为空
场景:快速查找且无需排序,如缓存系统。
代码案例:
#include <unordered_map>
int main() {
std::unordered_map<std::string, std::string> session_cache;
// 插入新会话
session_cache.emplace("user_1234", "token_abc");
session_cache.emplace("user_5678", "token_xyz");
// 查找并更新令牌
auto it = session_cache.find("user_1234");
if (it != session_cache.end()) {
it->second = "token_updated"; // 直接通过迭代器修改
}
return 0;
}
五、deque
:双端队列
函数介绍:
#include <deque>
/*dequeue 生成*/
dequeue<int> dq;
/*dequeue 头尾*/
dq.front();
dq.back();
/*dequeue 迭代器*/
dq.begin()
dq.end()
/*dequeue 插入*/
dq.push_front(2); //头插入
dq.push_back(2); //尾插入
/*dequeue 删除*/
dq.pop_front(); //头删除
dq.pop_back(); //尾删除
/*dequeue 容量*/
dq.size();
/*dequeue 判空*/
dq.empty()
/*dequeue 清空*/
dq.clear();
场景:需要同时在头部和尾部高效操作的队列。
代码案例:
1. 滑动窗口最大值(算法优化)
#include <iostream>
#include <deque>
#include <vector>
std::vector<int> maxSlidingWindow(const std::vector<int>& nums, int k) {
std::deque<int> dq; // 存储窗口内可能成为最大值的元素索引
std::vector<int> result;
for (int i = 0; i < nums.size(); ++i) {
// 移除不在当前窗口内的索引
if (!dq.empty() && dq.front() <= i - k) {
dq.pop_front();
}
// 维护递减序列(队首始终是当前窗口最大值)
while (!dq.empty() && nums[dq.back()] < nums[i]) {
dq.pop_back();
}
dq.push_back(i); // 插入当前元素索引
// 窗口形成后记录最大值
if (i >= k - 1) {
result.push_back(nums[dq.front()]);
}
}
return result;
}
int main() {
std::vector<int> nums = {1, 3, -1, -3, 5, 3, 6, 7};
auto res = maxSlidingWindow(nums, 3);
std::cout << "滑动窗口最大值: ";
for (int num : res) std::cout << num << " "; // 输出 3 3 5 5 6 7
}
2. 任务调度系统(优先处理紧急任务)
#include <deque>
#include <iostream>
class TaskScheduler {
private:
std::deque<std::string> urgent_tasks; // 头部插入紧急任务
std::deque<std::string> normal_tasks; // 尾部插入普通任务
public:
void add_task(const std::string& task, bool is_urgent) {
if (is_urgent) {
urgent_tasks.push_front(task); // 紧急任务插队
} else {
normal_tasks.push_back(task);
}
}
void process_tasks() {
while (!urgent_tasks.empty()) {
std::cout << "处理紧急任务: " << urgent_tasks.front() << std::endl;
urgent_tasks.pop_front();
}
while (!normal_tasks.empty()) {
std::cout << "处理普通任务: " << normal_tasks.front() << std::endl;
normal_tasks.pop_front();
}
}
};
int main() {
TaskScheduler scheduler;
scheduler.add_task("生成月报", false);
scheduler.add_task("修复BUG", true);
scheduler.process_tasks(); // 先处理修复BUG,再处理生成月报
}
六、stack
和queue
:适配器容器
一、stack
#include <stack>
/*stack 生成*/
stack<int> sk;
/*stack 插入*/
sk.push(2); //把一个元素放入栈
/*stack 删除*/
sk.pop(); //删除栈顶的元素
/*stack 栈顶*/
sk.top(); //返回栈顶元素
/*stack 容量*/
sk.size();
/*stack 判空*/
sk.empty()
#include <stack>
#include <string>
int main() {
std::stack<std::string> undo_stack;
undo_stack.push("原始文本");
undo_stack.push("添加标题");
undo_stack.push("修改段落");
// 执行两次撤销
std::cout << "当前操作:" << undo_stack.top() << "\n";
undo_stack.pop();
std::cout << "撤销后:" << undo_stack.top() << "\n";
return 0;
}
二、queue
#include <queue>
/*queue 生成*/
queue<int> q;
/*queue 头尾*/
q.front();
q.back();
/*queue 插入*/
q.push(2); //在队尾插入一个元素
/*queue 删除*/
q.pop(); //在队首删除一个元素
/*queue 容量*/
q.size();
/*queue 判空*/
q.empty()
#include <queue>
int main() {
std::queue<std::string> msg_queue;
msg_queue.push("用户登录");
msg_queue.push("数据上传");
msg_queue.push("生成报告");
// 处理所有消息
while (!msg_queue.empty()) {
std::cout << "正在处理:" << msg_queue.front() << "\n";
msg_queue.pop(); // 必须手动移除
}
return 0;
}
七、set
:有序唯一集合
函数介绍:
#include <set>
set<int> s;
// 插入与删除
s.insert(10);
s.emplace(20); // 原位构造元素
s.erase(10);
// 查找
auto pos = s.find(20); // 返回迭代器
if (pos != s.end()) s.erase(pos);
场景:去重且需要有序遍历,如黑名单过滤。
代码案例:
1. 数据去重与排序
#include <iostream>
#include <set>
#include <vector>
int main() {
std::vector<int> raw_data = {5, 3, 8, 1, 6, 3, 8};
std::set<int> unique_set(raw_data.begin(), raw_data.end());
std::cout << "去重后数据: ";
for (int num : unique_set) std::cout << num << " "; // 输出 1 3 5 6 8
}
2. 社交网络共同好友分析
#include <set>
#include <iostream>
int main() {
std::set<std::string> alice_friends = {"Bob", "Charlie", "Diana"};
std::set<std::string> bob_friends = {"Alice", "Charlie", "Eve"};
// 计算共同好友
std::set<std::string> mutual_friends;
for (const auto& name : alice_friends) {
if (bob_friends.count(name)) {
mutual_friends.insert(name);
}
}
std::cout << "共同好友: ";
for (const auto& name : mutual_friends) std::cout << name << " "; // 输出 Charlie
}
3.自定义排序规则(降序集合)
#include <set>
#include <functional> // std::greater
int main() {
std::set<int, std::greater<int>> desc_set;
desc_set.insert(5);
desc_set.insert(2);
desc_set.insert(8);
std::cout << "降序集合: ";
for (int num : desc_set) std::cout << num << " "; // 输出 8 5 2
}