C++: STL 容器基础

前面的课程里,我们学了模板——让函数和类支持任意类型。

但真实项目中,你不需要自己写容器(如动态数组、链表)。

C++ 标准模板库(STL)已经提供了现成的容器,直接用就行!


1. 什么是 STL?

STL(Standard Template Library,标准模板库)是 C++ 标准库的一部分,包含:

组成部分 作用 示例
容器 存数据 std::vectorstd::array
迭代器 遍历容器 begin()end()
算法 操作数据 std::sortstd::find
函数对象 自定义比较逻辑 std::lessstd::greater

💡 重点: STL 是模板实现的,所以能支持任意类型。



2. vector(最常用)

(1) 2.1 什么是 vector?

std::vector动态数组——大小能自动增长。

对比 普通数组 vector
大小 固定 动态
内存 栈或堆
访问元素 arr[i] vec[i]vec.at(i)
推荐度 ⭐⭐ ⭐⭐⭐⭐⭐

▶ 示例 1:vector 基本用法(难度⭐)

CPP
#include <iostream>
#include <vector>

int main() {
 // 创建一个存 int 的 vector
 std::vector<int> vec = {1, 2, 3, 4, 5};
 
 // 访问元素
 std::cout << "第一个元素:" << vec[0] << std::endl;
 std::cout << "第二个元素:" << vec.at(1) << std::endl;
 
 // 修改元素
 vec[0] = 100;
 
 // 获取大小
 std::cout << "大小:" << vec.size() << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
第一个元素:1
第二个元素:2
大小:5

💡 提示: vec.at(i)检查越界,如果越界会抛异常;vec[i] 不检查,效率高。


(2) 2.2 添加和删除元素

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> vec;
 
 // 添加元素到末尾
 vec.push_back(10);
 vec.push_back(20);
 vec.push_back(30);
 
 // 删除末尾元素
 vec.pop_back();
 
 // 在指定位置插入元素
 vec.insert(vec.begin() + 1, 15); // 在第二个位置插入 15
 
 // 删除指定位置的元素
 vec.erase(vec.begin() + 1); // 删除第二个元素
 
 return 0;
}

(3) 2.3 遍历 vector

方法一:下标遍历(最常用)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> vec = {1, 2, 3, 4, 5};
 
 for (size_t i = 0; i < vec.size(); i++) {
 std::cout << vec[i] << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

方法二:范围 for(C++11,推荐)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> vec = {1, 2, 3, 4, 5};
 
 for (int x : vec) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

方法三:迭代器(后面会学)

CPP
#include <iostream>
#include <vector>

int main() {
 std::vector<int> vec = {1, 2, 3, 4, 5};
 
 for (auto it = vec.begin(); it != vec.end(); ++it) {
 std::cout << *it << " ";
 }
 std::cout << std::endl;
 
 return 0;
}

💡 推荐: 优先用范围 for(C++11),最简洁。



3. array(固定大小数组)

(1) 3.1 什么是 array?

std::array固定大小的数组,但比普通数组更安全。

对比 普通数组 array
大小 要知道 size() 获取
会不会退化成指针 不会
推荐度 ⭐⭐ ⭐⭐⭐⭐

▶ 示例 2:array 基本用法(难度⭐)

CPP
#include <iostream>
#include <array>

int main() {
 // 创建一个存 5 个 int 的 array
 std::array<int, 5> arr = {1, 2, 3, 4, 5};
 
 // 访问元素
 std::cout << "第一个元素:" << arr[0] << std::endl;
 
 // 获取大小
 std::cout << "大小:" << arr.size() << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
第一个元素:1
大小:5

💡 重点: std::array 的大小是编译时确定的,不能改变。



4. deque(双端队列)

(1) 4.1 什么是 deque?

std::deque双端队列——能在两端快速添加/删除元素。

操作 vector deque
末尾添加 O(1) O(1)
头部添加 O(n) O(1)
随机访问 O(1) O(1)

▶ 示例 3:deque 基本用法(难度⭐⭐)

CPP
#include <iostream>
#include <deque>

int main() {
 std::deque<int> dq;
 
 // 在末尾添加
 dq.push_back(10);
 dq.push_back(20);
 
 // 在头部添加
 dq.push_front(5);
 dq.push_front(1);
 
 // 现在 dq 是:1, 5, 10, 20
 
 for (int x : dq) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
1 5 10 20


5. list(双向链表)

(1) 5.1 什么是 list?

std::list双向链表——每个元素都存着前后元素的地址。

对比 vector list
随机访问 O(1) ❌ 不支持
中间插入/删除 O(n) O(1)
内存占用 大(每个元素多两个指针)

▶ 示例 4:list 基本用法(难度⭐⭐)

CPP
#include <iostream>
#include <list>

int main() {
 std::list<int> lst = {1, 2, 3, 4, 5};
 
 // 在开头添加
 lst.push_front(0);
 
 // 在末尾添加
 lst.push_back(6);
 
 // 删除值为 3 的元素
 lst.remove(3);
 
 for (int x : lst) {
 std::cout << x << " ";
 }
 std::cout << std::endl;
 
 return 0;
}
▶ 试一试

输出:

TEXT 📖 仅展示
0 1 2 4 5 6


6. forward_list(单向链表)

(1) 6.1 什么是 forward_list?

std::forward_list单向链表——每个元素只存下一个元素的地址。

对比 list forward_list
内存占用 较大 较小
能往前遍历吗?
推荐度 ⭐⭐⭐⭐ ⭐⭐(特定场景用)

💡 建议: 除非你确定只需要往后遍历,否则用 std::list



7. 容器选择指南

场景 推荐容器
需要动态大小 std::vector
大小固定,要保留 C 风格 std::array
需要在头部快速添加/删除 std::deque
需要频繁在中间插入/删除 std::list
只需要往后遍历,且要省内存 std::forward_list

💡 黄金法则: 优先用 std::vector,除非有明确理由用别的。



8. 实战:用 vector 实现动态数组(难度⭐⭐)

CPP
#include <iostream>
#include <vector>
#include <string>

struct Student {
 std::string name;
 int age;
 double score;
};

int main() {
 std::vector<Student> students;
 
 // 添加学生
 students.push_back({"Alice", 20, 92.5});
 students.push_back({"Bob", 21, 88.0});
 students.push_back({"Charlie", 19, 95.0});
 
 // 遍历并输出
 for (const auto& s : students) {
 std::cout << "姓名:" << s.name 
 << ",年龄:" << s.age 
 << ",成绩:" << s.score << std::endl;
 }
 
 return 0;
}

❓ 常见问题

Q:vector 和 array 有什么区别? A:> - vector 大小动态,存在堆上 > - array 大小固定,存在栈上 > > 选择建议: 如果大小不确定,用 vector;如果大小固定且较小,用 array。 Q:为什么遍历 vector 推荐用范围 for? A:因为更简洁,且不容易写错循环条件。 > > // 传统 for(容易写错条件) > for (size_t i = 0; i < vec.size(); i++) { ... } > > // 范围 for(简洁,不易错) > for (int x : vec) { ... } > Q:list 和 vector 哪个快? A:看场景: > - 如果需要随机访问(vec[100]),vector 快 > - 如果需要在中间插入/删除,list


Q:关于STL containers最重要的是什么? A:先理解核心概念,再通过实践例子来巩固。

📖 小节


📝 作业

  1. 基础题 (Difficulty ⭐):std::vector<int> 存 5 个整数,遍历并输出。

  2. 进阶题 (Difficulty ⭐⭐):std::vector<std::string> 存 3 个字符串,让用户输入,然后输出。

  3. 挑战题 (Difficulty ⭐⭐⭐):std::deque 实现" palindrome 检测":

  4. 从两端往中间比,如果所有对应字符都相等,就是 palindrome

  5. 示例:"racecar" 是 palindrome,"hello" 不是


9. 🚀 下一步

学会了 STL 容器基础,接下来我们学习 STL 算法(第35课)—— 用标准库提供的算法操作容器,不用自己写排序、查找……

Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏