1. C++ STL list容器概述
在C++标准模板库(STL)中,list是一个双向链表容器,它允许在常数时间内进行任意位置的插入和删除操作。与vector这种连续存储的容器不同,list的元素在内存中是非连续存储的,每个元素都包含指向前驱和后继的指针。
list特别适合以下场景:
- 需要频繁在序列中间插入/删除元素
- 不需要随机访问元素(即不需要通过下标快速访问)
- 需要保证插入和删除操作不会使其他元素的迭代器失效(除了被删除的元素)
我刚开始使用list时,常常纠结它与vector的选择。后来发现一个简单原则:如果需要频繁在中间插入删除就用list,如果需要快速随机访问就用vector。这个经验帮我避免了很多性能问题。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. list的基本构造方法
2.1 默认构造函数
最简单的创建list的方式是使用默认构造函数:
cpp复制std::list<int> myList; // 创建一个空的int类型list
这个空的list不包含任何元素,但已经准备好接收元素。在实际项目中,我习惯先创建空list,再根据业务逻辑逐步填充数据,这样可以避免不必要的内存分配。
2.2 使用初始元素个数构造
我们可以指定初始元素个数和可选的值初始化:
cpp复制std::list<std::string> names(5); // 5个空字符串
std::list<double> values(10, 3.14); // 10个3.14
这里有个坑需要注意:当使用元素个数构造时,如果元素类型是类且有显式构造函数,会调用默认构造函数进行初始化。我曾经遇到过因为误解这点导致的性能问题。
2.3 使用迭代器范围构造
list支持通过其他容器的迭代器范围来构造:
cpp复制std::vector<int> vec = {1, 2, 3, 4, 5};
std::list<int> myList(vec.begin(), vec.end());
这种构造方式非常灵活,可以方便地在不同容器类型间转换数据。我在处理数据转换时经常使用这种方法。
2.4 拷贝构造函数
list支持通过另一个list来构造:
cpp复制std::list<int> original = {1, 2, 3};
std::list<int> copy(original); // 深拷贝
拷贝构造会创建元素的完整副本,这在需要隔离数据修改时非常有用。
2.5 移动构造函数(C++11)
C++11引入了移动语义,list也支持移动构造:
cpp复制std::list<int> createList() {
std::list<int> temp = {1, 2, 3};
return temp; // 触发移动构造
}
std::list<int> myList = createList(); // 高效转移资源
移动构造避免了不必要的拷贝,对于大型list可以显著提高性能。我在性能敏感的场景中会特别注意使用移动语义。
3. list的基本操作
3.1 元素访问
list不支持随机访问,只能通过迭代器或front()/back()访问元素:
cpp复制std::list<int> nums = {1, 2, 3, 4, 5};
std::cout << nums.front(); // 1
std::cout << nums.back(); // 5
// 遍历list
for(auto it = nums.begin(); it != nums.end(); ++it) {
std::cout << *it << " ";
}
注意:试图访问空list的front()或back()会导致未定义行为。我曾在项目中因此导致程序崩溃,现在总是先检查empty()。
3.2 插入元素
list提供了多种插入方式:
cpp复制std::list<int> nums = {1, 2, 3};
nums.push_front(0); // 头部插入: {0,1,2,3}
nums.push_back(4); // 尾部插入: {0,1,2,3,4}
auto it = nums.begin();
++it;
nums.insert(it, 10); // 在第二个位置插入10: {0,10,1,2,3,4
