【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
文章目录
- C++ `list` 容器详解:从入门到精通
- 前言
- 第一章:C++ `list` 容器简介
- 1.1 C++ STL 容器概述
- 1.2 `list` 的特点
- 第二章:`list` 的构造方法
- 2.1 常见构造函数
- 2.1.1 示例:不同构造方法
- 2.1.2 相关文档
- 第三章:`list` 迭代器的使用
- 3.1 常见迭代器
- 3.1.1 示例:使用正向和反向迭代器遍历 `list`
- 3.1.2 相关文档
- 第四章:`list` 的容量与大小操作
- 4.1 容量管理接口
- 4.1.1 示例:容量操作
- 4.1.2 相关文档
- 第五章:`list` 的元素访问
- 5.1 元素访问方法
- 5.1.1 示例:访问第一个与最后一个元素
- 5.1.2 相关文档
- 第六章:`list` 的插入、删除与修改
- 6.1 插入操作
- 6.1.1 示例:使用 `push_back()` 和 `push_front()` 插入元素
- 6.1.2 示例:使用 `insert()` 在指定位置插入元素
- 6.1.3 插入元素的常见问题
- 6.1.4 相关文档
- 6.2 删除操作
- 6.2.1 示例:删除 `list` 中的首尾元素
- 6.2.2 示例:删除指定位置的元素
- 6.2.3 示例:清空 `list`
- 6.2.4 删除操作的常见问题
- 6.2.5 相关文档
- 6.3 修改操作
- 6.3.1 示例:修改 `list` 中的首尾元素
- 6.3.2 示例:通过迭代器修改 `list` 中的元素
- 6.3.3 修改操作的常见问题
- 第七章:`list` 的迭代器失效问题
- 7.1 删除操作导致的迭代器失效
- 7.1.1 示例:删除元素时正确的迭代器处理
- 7.1.2 错误示例:删除后不更新迭代器
- 7.1.3 相关文档
- 第八章:`list` 常见的其他修改操作
- 8.1 `splice()` 操作
- 8.1.1 示例:使用 `splice()` 操作
- 8.1.2 相关文档
- 8.2 `merge()` 操作
- 8.2.1 示例:使用 `merge()` 操作
- 8.2.2 相关文档
- 第九章:`list` 的排序与去重
- 9.1 `sort()` 操作
- 9.1.1 示例:对 `list` 进行排序
- 9.1.2 使用自定义比较函数排序
- 9.2 `unique()` 操作
- 9.2.1 示例:使用 `unique()` 去重
- 9.2.2 使用自定义规则去重
- 第十章:`list` 的其他操作
- 10.1 `reverse()` 操作
- 10.1.1 示例:反转 `list` 中的元素
- 10.1.2 相关文档
- 10.2 `swap()` 操作
- 10.2.1 示例:交换两个 `list` 的内容
- 11.2.2 相关文档
- 10.3 `remove()` 操作
- 10.3.1 示例:移除指定值的元素
- 10.3.2 相关文档
- 10.4 `remove_if()` 操作
- 10.4.1 示例:使用 `remove_if()` 删除符合条件的元素
- 10.4.2 相关文档
- 10.5 `emplace()` 和 `emplace_back()` 操作
- 10.5.1 示例:使用 `emplace()` 和 `emplace_back()`
- 10.5.2 相关文档
- 第十一章:`list` 的内存管理
- 11.1 `shrink_to_fit()` 操作
- 写在最后
C++ list
容器详解:从入门到精通
💬 欢迎讨论:学习过程中有问题吗?随时在评论区与我交流。你们的互动是我创作的动力!
👍 支持我:如果你觉得这篇文章对你有帮助,请点赞、收藏并分享给更多朋友吧!
🚀 一起成长:欢迎分享给更多对 C++ 感兴趣的小伙伴,让我们共同进步!
前言
C++ 标准模板库(STL)中的 list
容器是一个双向链表结构,它提供了高效的插入和删除操
作。与 vector
不同,list
中的元素不是连续存储的,因此可以在任何位置高效插入和删除元素,而无需移动其他元素。虽然它在随机访问方面不如 vector
高效,但在大量的插入和删除操作场景中具有不可替代的优势。
本文将通过详细的示例代码,从基础到进阶,逐步讲解如何使用 C++ 中的 list
容器,并探讨其特性与常用操作。
第一章:C++ list
容器简介
1.1 C++ STL 容器概述
C++ 提供了丰富的标准模板库 (STL),其中包括顺序容器(如 vector
、deque
)和关联容器(如 map
、set
)。list
是一种链表结构的顺序容器,它的底层实现是双向链表。这使得 list
在插入和删除操作上比 vector
更加高效,但由于不支持随机访问,因此访问特定位置的元素时效率较低。
1.2 list
的特点
- 双向链表:
list
底层是一个双向链表,能够高效地进行插入和删除操作。 - 不支持随机访问:由于链表的结构特点,
list
只能顺序访问,随机访问效率低下。 - 动态增长:
list
不需要预留空间,它会根据需要动态分配内存。
#include <list>
#include <iostream>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};for (int val : lst) {cout << val << " ";}return 0;
}
第二章:list
的构造方法
2.1 常见构造函数
C++
list
提供了多种构造函数,允许用户根据不同需求初始化链表。
构造函数 | 功能 |
---|---|
list() | 构造一个空的 list |
list(size_type n, const T& val) | 构造一个包含 n 个值为 val 的元素的 list |
list(const list& x) | 拷贝构造函数,构造与 x 相同的 list |
list(InputIterator first, InputIterator last) | 使用 [first, last) 区间内的元素构造 list |
2.1.1 示例:不同构造方法
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst1; // 空 listlist<int> lst2(5, 100); // 5个值为100的元素list<int> lst3(lst2); // 拷贝构造list<int> lst4 = {1, 2, 3, 4, 5}; // 初始化列表for (int val : lst4) {cout << val << " "; // 输出: 1 2 3 4 5}return 0;
}
2.1.2 相关文档
- C++ Reference: list constructor
第三章:list
迭代器的使用
list
支持多种迭代器类型,允许我们遍历、访问和修改链表中的元素。迭代器可以看作指向list
中节点的指针,遍历时可以用迭代器依次访问链表中的每一个节点。
3.1 常见迭代器
迭代器类型 | 功能 |
---|---|
begin() | 返回指向链表第一个元素的迭代器 |
end() | 返回指向链表末尾的迭代器 |
rbegin() | 返回指向链表最后一个元素的反向迭代器 |
rend() | 返回指向链表第一个元素之前的反向迭代器 |
cbegin() | 返回常量迭代器,不能修改元素 |
cend() | 返回常量迭代器,指向链表末尾 |
3.1.1 示例:使用正向和反向迭代器遍历 list
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 使用正向迭代器遍历for (auto it = lst.begin(); it != lst.end(); ++it) {cout << *it << " "; // 输出: 1 2 3 4 5}cout << endl;// 使用反向迭代器遍历for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) {cout << *rit << " "; // 输出: 5 4 3 2 1}cout << endl;return 0;
}
3.1.2 相关文档
- C++ Reference: list iterator
第四章:list
的容量与大小操作
4.1 容量管理接口
list
提供了常用的容量管理接口,方便用户操作链表的大小和判断链表状态。
方法名 | 功能描述 |
---|---|
empty() | 检测 list 是否为空 |
size() | 返回 list 中元素的数量 |
max_size() | 返回 list 可容纳的最大元素数 |
resize(n) | 调整 list 的大小为 n |
4.1.1 示例:容量操作
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};cout << "Size: " << lst.size() << endl; // 输出当前元素个数cout << "Is empty: " << (lst.empty() ? "Yes" : "No") << endl; // 判断是否为空lst.resize(3); // 调整大小为3,保留前3个元素for (int val : lst) {cout << val << " "; // 输出: 1 2 3}return 0;
}
4.1.2 相关文档
- C++ Reference: list size
第五章:list
的元素访问
5.1 元素访问方法
list
提供了几种常用的方法用于访问链表中的元素。
方法名 | 功能 |
---|---|
front() | 返回 list 的第一个元素 |
back() | 返回 list 的最后一个元素 |
5.1.1 示例:访问第一个与最后一个元素
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};cout << "First element: " << lst.front() << endl; // 访问第一个元素cout << "Last element: " << lst.back() << endl; // 访问最后一个元素return 0;
}
5.1.2 相关文档
- C++ Reference: list element access
第六章:list
的插入、删除与修改
6.1 插入操作
list
容器提供了多种插入操作,包括在前部、尾部插入元素,或在指定位置插入。与 vector
不同的是,list
插入时不需要移动其他元素,只修改指针,因此插入效率非常高。
方法名 | 功能描述 |
---|---|
push_front() | 在 list 的前部插入元素 |
push_back() | 在 list 的末尾插入元素 |
insert(position, val) | 在指定位置插入元素 |
6.1.1 示例:使用 push_back()
和 push_front()
插入元素
push_front()
和 push_back()
是将元素插入到链表前部和尾部的常用方法。由于 list
是双向链表,头部和尾部操作的效率都非常高,为 O(1)。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3};// 在前部插入元素lst.push_front(0);// 在末尾插入元素lst.push_back(4);for (int val : lst) {cout << val << " "; // 输出: 0 1 2 3 4}return 0;
}
6.1.2 示例:使用 insert()
在指定位置插入元素
insert()
用于在链表中指定位置插入元素。该方法需要提供一个迭代器指向要插入的位置。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 3, 4};// 在第二个位置插入2auto it = lst.begin();++it;lst.insert(it, 2);for (int val : lst) {cout << val << " "; // 输出: 1 2 3 4 }return 0;
}
6.1.3 插入元素的常见问题
- 迭代器失效:在
list
中进行插入操作时,插入不会使已有迭代器失效,因为list
是双向链表,插入时只修改指针。 - 尾部插入效率:在链表尾部插入元素的效率始终为 O(1),无需移动其他元素,这点不同于
vector
。 - 插入到特定位置的效率:虽然
insert()
操作本身是 O(1),但查找特定插入位置的时间复杂度是 O(n),这取决于你如何获取迭代器。
6.1.4 相关文档
- C++ Reference: list insertions
6.2 删除操作
list
提供了多种删除元素的方式,包括从前部和尾部删除,删除指定位置的元素,以及一次性清空整个链表。
方法名 | 功能描述 |
---|---|
pop_front() | 删除 list 的第一个元素 |
pop_back() | 删除 list 的最后一个元素 |
erase() | 删除指定位置的元素 |
clear() | 清空 list |
6.2.1 示例:删除 list
中的首尾元素
pop_front()
和 pop_back()
用于删除 list
中的第一个或最后一个元素。与插入操作类似,这两种操作的时间复杂度都是 O(1),不会影响其他元素的指针。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 删除第一个元素lst.pop_front();// 删除最后一个元素lst.pop_back();for (int val : lst) {cout << val << " "; // 输出: 2 3 4}return 0;
}
6.2.2 示例:删除指定位置的元素
erase()
用于删除指定位置的元素。它需要提供一个指向该位置的迭代器。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 查找要删除的元素auto it = lst.begin();advance(it, 2); // 移动到第三个元素// 删除第三个元素lst.erase(it);for (int val : lst) {cout << val << " "; // 输出: 1 2 4 5}return 0;
}
6.2.3 示例:清空 list
clear()
是一种非常彻底的清除操作,它会删除 list
中的所有元素。值得注意的是,clear()
仅会删除有效节点,不会删除链表的头节点(即 list
对象本身)。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 清空 listlst.clear();cout << "Size after clear: " << lst.size() << endl; // 输出: 0cout << "Is list empty? " << (lst.empty() ? "Yes" : "No") << endl; // 输出: Yesreturn 0;
}
6.2.4 删除操作的常见问题
- 迭代器失效:在
list
中,删除操作只会导致指向被删除元素的迭代器失效,其他迭代器不受影响。删除后如果需要继续使用迭代器,应该使用erase()
的返回值,指向下一个有效元素。 - clear() 是否删除头节点:
clear()
不会删除list
的头节点。调用clear()
后,list
对象依然存在,只是里面的所有元素被删除,list
的结构保持完好。
6.2.5 相关文档
- C++ Reference: list clear
- C++ Reference: list erase
- C++ Reference: list pop_back
6.3 修改操作
通过迭代器或者
list
提供的访问接口,用户可以直接修改链表中的元素。由于list
不支持随机访问,所以修改操作通常需要遍历元素。
方法名 | 功能描述 |
---|---|
front() | 返回 list 中第一个元素 |
back() | 返回 list 中最后一个元素 |
迭代器 | 通过迭代器访问修改元素 |
6.3.1 示例:修改 list
中的首尾元素
通过 front()
和 back()
,可以分别访问并修改 list
中的第一个和最后一个元素。修改操作的时间复杂度为 O(1)。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 修改第一个元素lst.front() = 10;// 修改最后一个元素lst.back() = 20;for (int val : lst) {cout << val << " "; // 输出: 10 2 3 4 20}return 0;
}
6.3.2 示例:通过迭代器修改 list
中的元素
由于 list
不支持随机访问,修改中间位置的元素需要通过迭代器遍历找到目标位置。
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 使用迭代器修改第三个元素auto it = lst.begin();advance(it, 2); // 移动到第三个元素*it = 30;for (int val : lst) {cout << val << " "; // 输出: 1 2 30 4 5}return 0;
}
6.3.3 修改操作的常见问题
- 效率问题:由于
list
是链表结构,访问中间元素时无法像vector
一样通过下标随机访问,而是必须通过迭代器进行遍历,时间复杂度为 O(n)。 advance()
函数用于将迭代器向前或向后移动指定的距离,这是list
中最常用的访问与修改元素方式之一。由于list
不能通过下标随机访问,迭代器的使用显得尤为重要。- 避免无效访问:通过迭代器进行修改时,确保在修改过程中没有删除操作,否则迭代器可能失效,导致未定义行为。
第七章:list
的迭代器失效问题
list
的底层实现为双向链表,因此与vector
不同,list
的插入和删除操作不会导致整体迭代器失效。具体来说:
- 插入操作:不会导致现有迭代器失效。
- 删除操作:仅导致被删除元素的迭代器失效,其他迭代器不会受影响。
7.1 删除操作导致的迭代器失效
删除操作会使指向被删除元素的迭代器失效,如果在删除元素后继续使用失效的迭代器,将会导致程序的未定义行为。因此,在执行删除操作后,我们必须重新更新迭代器。
7.1.1 示例:删除元素时正确的迭代器处理
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 查找并删除元素3auto it = lst.begin();while (it != lst.end()) {if (*it == 3) {it = lst.erase(it); // 删除元素并获取下一个有效迭代器} else {++it; // 继续遍历}}for (int val : lst) {cout << val << " "; // 输出: 1 2 4 5}return 0;
}
在上面的代码中,
erase()
函数会返回一个指向被删除元素之后的迭代器,因此我们使用该返回值继续遍历。这是一种常见的迭代器删除操作的最佳实践,可以避免迭代器失效问题。
7.1.2 错误示例:删除后不更新迭代器
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};auto it = lst.begin();while (it != lst.end()) {if (*it == 3) {lst.erase(it); // 删除元素,但未更新迭代器++it; // 错误:it 已经失效,导致未定义行为} else {++it;}}return 0;
}
在这个错误的示例中,删除操作使 it
失效,但我们在下一个循环中继续使用了失效的 it
,这会导致未定义行为,可能会引发程序崩溃。
7.1.3 相关文档
- C++ Reference: list erase
第八章:list
常见的其他修改操作
8.1 splice()
操作
splice()
是list
特有的操作,它允许我们将一个list
中的元素直接拼接到另一个list
中,而不会重新分配内存或复制元素。该操作非常高效,因为它仅修改链表的指针。
方法名 | 功能描述 |
---|---|
splice(position, x) | 将 list x 的所有元素插入到当前 list 中 |
splice(position, x, it) | 将 list x 中的 it 指定的元素插入到当前 list 中 |
splice(position, x, first, last) | 将 x 中 [first, last) 区间的元素插入当前 list |
8.1.1 示例:使用 splice()
操作
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst1 = {1, 2, 3};list<int> lst2 = {4, 5, 6};// 将 lst2 的元素拼接到 lst1 的末尾lst1.splice(lst1.end(), lst2);for (int val : lst1) {cout << val << " "; // 输出: 1 2 3 4 5 6}cout << "\nList 2 size: " << lst2.size() << endl; // 输出: 0 (lst2 已被清空)return 0;
}
splice()
可以高效地将一个链表中的元素移动到另一个链表中,它不会复制元素,也不会破坏链表的连续性。
8.1.2 相关文档
- C++ Reference: list splice
8.2 merge()
操作
merge()
函数用于将两个已经排序好的list
合并为一个有序的list
。它会自动按照升序或自定义的比较规则合并两个链表。
方法名 | 功能描述 |
---|---|
merge(list& x) | 将已排序的 x 合并到当前链表中 |
merge(list& x, Compare comp) | 使用自定义比较函数 comp 合并 x |
8.2.1 示例:使用 merge()
操作
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst1 = {1, 3, 5};list<int> lst2 = {2, 4, 6};// 合并两个已排序的链表lst1.merge(lst2);for (int val : lst1) {cout << val << " "; // 输出: 1 2 3 4 5 6}return 0;
}
merge()
会将两个有序链表合并成一个新的有序链表,并且不会对原链表进行元素的复制,只是对链表节点进行了重新连接。
8.2.2 相关文档
- C++ Reference: list merge
第九章:list
的排序与去重
9.1 sort()
操作
list
提供了sort()
函数来对链表进行排序。由于list
不支持随机访问,因此它使用的排序算法是稳定的归并排序,性能为 O(N log N)。
方法名 | 功能描述 |
---|---|
sort() | 默认按照升序排序 |
sort(Compare comp) | 使用自定义比较函数 comp 进行排序 |
9.1.1 示例:对 list
进行排序
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {5, 2, 9, 1, 5, 6};// 对链表进行排序lst.sort();for (int val : lst) {cout << val << " "; // 输出: 1 2 5 5 6 9}return 0;
}
9.1.2 使用自定义比较函数排序
#include <iostream>
#include <list>
using namespace std;bool customCompare(int a, int b) {return a > b; // 降序比较
}int main() {list<int> lst = {5, 2, 9, 1, 5, 6};// 使用自定义比较函数进行降序排序lst.sort(customCompare);for (int val : lst) {cout << val << " "; // 输出: 9 6 5 5 2 1}return 0;
}
9.2 unique()
操作
unique()
函数用于去除链表中相邻的重复元素。它会比较相邻的两个元素,如果它们相等,则删除后一个元素。
方法名 | 功能描述 |
---|---|
unique() | 移除相邻的重复元素 |
unique(BinaryPredicate p) | 使用自定义的比较规则 p 移除相邻的元素 |
9.2.1 示例:使用 unique()
去重
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 1, 2, 3, 3, 4, 5, 5};// 去除相邻的重复元素lst.unique();for (int val : lst) {cout << val << " "; // 输出: 1 2 3 4 5}return 0;
}
9.2.2 使用自定义规则去重
#include <iostream>
#include <list>
using namespace std;bool customEqual(int a, int b) {return a % 2 == b % 2; // 自定义规则:移除相邻的偶数/奇数
}int main() {list<int> lst = {1, 3, 2, 4, 5, 6};// 使用自定义规则去重lst.unique(customEqual);for (int val : lst) {cout << val << " "; // 输出: 1 2 5}return 0;
}
第十章:list
的其他操作
10.1 reverse()
操作
reverse()
函数用于将list
的顺序进行反转。该操作不会创建新的链表,而是直接修改现有链表的链接顺序。
方法名 | 功能描述 |
---|---|
reverse() | 将 list 中的元素顺序反转 |
10.1.1 示例:反转 list
中的元素
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 5};// 反转 list 中的元素lst.reverse();for (int val : lst) {cout << val << " "; // 输出: 5 4 3 2 1}return 0;
}
通过 reverse()
函数,原本顺序存储的元素将被反转,链表中的第一个元素变为最后一个,最后一个变为第一个。
10.1.2 相关文档
- C++ Reference: list reverse
10.2 swap()
操作
swap()
函数用于交换两个list
容器的内容。这个操作非常高效,因为list
只交换内部的指针和相关数据,而不会实际移动或复制元素。
方法名 | 功能描述 |
---|---|
swap(list& x) | 交换当前 list 与 x 中的元素 |
10.2.1 示例:交换两个 list
的内容
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst1 = {1, 2, 3};list<int> lst2 = {4, 5, 6};// 交换两个 listlst1.swap(lst2);cout << "List 1: ";for (int val : lst1) {cout << val << " "; // 输出: 4 5 6}cout << "\nList 2: ";for (int val : lst2) {cout << val << " "; // 输出: 1 2 3}return 0;
}
swap()
是一种非常高效的操作,尤其是在需要大量数据交换时,可以避免拷贝开销。
11.2.2 相关文档
- C++ Reference: list swap
10.3 remove()
操作
remove()
函数用于从list
中移除所有与指定值相等的元素。它会遍历整个链表,删除所有匹配的元素。
方法名 | 功能描述 |
---|---|
remove(const T& val) | 删除所有与 val 相等的元素 |
10.3.1 示例:移除指定值的元素
#include <iostream>
#include <list>
using namespace std;int main() {list<int> lst = {1, 2, 3, 4, 2, 5};// 移除值为2的所有元素lst.remove(2);for (int val : lst) {cout << val << " "; // 输出: 1 3 4 5}return 0;
}
remove()
函数会移除链表中所有等于指定值的元素。由于链表是双向的,这种操作不会导致大量的数据移动,只是修改指针指向。
10.3.2 相关文档
- C++ Reference: list remove
10.4 remove_if()
操作
remove_if()
函数根据给定的条件(谓词)移除链表中符合条件的所有元素。与remove()
不同,它可以使用自定义的判断规则来删除元素。
方法名 | 功能描述 |
---|---|
remove_if(UnaryPredicate p) | 移除所有满足谓词 p 条件的元素 |
10.4.1 示例:使用 remove_if()
删除符合条件的元素
#include <iostream>
#include <list>
using namespace std;// 判断条件:删除所有偶数
bool isEven(int n) {return n % 2 == 0;
}int main() {list<int> lst = {1, 2, 3, 4, 5, 6};// 删除所有偶数元素lst.remove_if(isEven);for (int val : lst) {cout << val << " "; // 输出: 1 3 5}return 0;
}
在这个例子中,remove_if()
根据自定义的谓词函数 isEven()
删除了链表中所有的偶数元素。
10.4.2 相关文档
- C++ Reference: list remove_if
10.5 emplace()
和 emplace_back()
操作
emplace()
和emplace_back()
是list
提供的构造元素的方法,它们允许我们直接在链表中构造元素,避免不必要的复制操作。相比push_back()
,emplace_back()
更加高效,尤其是在插入复杂对象时。
方法名 | 功能描述 |
---|---|
emplace(position, args...) | 在指定位置直接构造元素 |
emplace_back(args...) | 在链表末尾直接构造元素,避免复制构造开销 |
10.5.1 示例:使用 emplace()
和 emplace_back()
#include <iostream>
#include <list>
using namespace std;struct Point {int x, y;Point(int a, int b) : x(a), y(b) {}
};int main() {list<Point> points;// 在 list 中直接构造元素points.emplace_back(1, 2); // 在末尾构造元素 (1, 2)points.emplace(points.begin(), 3, 4); // 在起始位置构造元素 (3, 4)for (const auto& pt : points) {cout << "(" << pt.x << ", " << pt.y << ") "; // 输出: (3, 4) (1, 2)}return 0;
}
emplace()
和 emplace_back()
提供了更灵活和高效的插入方式,尤其在处理复杂对象时可以减少额外的构造和复制操作。
10.5.2 相关文档
- C++ Reference: list emplace
第十一章:list
的内存管理
11.1 shrink_to_fit()
操作
list
不像vector
那样需要经常处理容量管理和扩容问题,因为它的底层实现是链表,元素的插入和删除并不会影响容器的容量分配。但 STL 容器通常提供shrink_to_fit()
函数来缩减不必要的内存开销,而list
没有此函数,因为链表结构本身并不涉及到多余的容量分配问题。
写在最后
本文详尽介绍了 C++ STL 中 list
容器的各类操作。我们从基本的构造、元素访问、容量管理,到迭代器、修改操作、排序与去重等高级功能,深入讲解了如何使用 list
实现高效的插入、删除和操作。同时我们也讨论了 list
特有的操作如 splice()
、merge()
、remove()
等。
在 C++ 中,list
作为双向链表,非常适合频繁插入和删除元素的场景,但它不支持随机访问,这与 vector
的应用场景有所不同。在实际开发中,可以根据需要选择合适的容器来优化性能和提高程序的可读性。
💬 欢迎讨论:如果你有任何问题或建议,请在评论区留言。
👍 支持一下:如果你觉得这篇文章对你有帮助,请点赞、收藏并分享!你们的支持是我持续创作的动力!
以上就是关于【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️
相关文章:

【C++篇】深度剖析C++ STL:玩转 list 容器,解锁高效编程的秘密武器
文章目录 C list 容器详解:从入门到精通前言第一章:C list 容器简介1.1 C STL 容器概述1.2 list 的特点 第二章:list 的构造方法2.1 常见构造函数2.1.1 示例:不同构造方法2.1.2 相关文档 第三章:list 迭代器的使用3.1 …...

植物大战僵尸杂交版V2.5.1下载(最新版)
2.5.1版本更新公告: 在最新的2.5.1版本中,游戏对“两面夹击”关卡进行了多项重要调整。出怪倍率和种类均有所降低,部分关卡的初始阳光量也得到了调整,以增强玩家的策略性。同时,玩家可以在这些关卡中使用投手类植物&a…...

基于nodejs+vue的游戏陪玩系统
作者:计算机学姐 开发技术:SpringBoot、SSM、Vue、MySQL、JSP、ElementUI、Python、小程序等,“文末源码”。 专栏推荐:前后端分离项目源码、SpringBoot项目源码、Vue项目源码、SSM项目源码 精品专栏:Java精选实战项目…...

SVN文件不显示修改状态图标
今天安装试用SVN时发现文件不显示修改状态 以下为解决方法: 1,在有.svn的文件夹中右键--tortoiseSvn--setting 2,选中icon Overlays,右侧的status cache 选shell 3,点击icon set 如下图所示 4,修改icon…...

GB28181语音对讲协议详解
GB28181-2016语音对讲流程如下图1所示: 图1.语音对讲流程。 其中, 信令 1 、2 、 3 、 4 为语音广播通知、 语音广播应答消息流程; 信令 5 、 1 2 、 1 3 、 1 4 、 1 5 、 1 6 为 S I P 服务器接收到客户端的呼叫请求通过 B 2 B UA 代理方式建立语音流接收者与媒…...

JavaScript 数据可视化:前端开发的核心工具
随着互联网和大数据的快速发展,数据呈爆炸式增长,如何有效地展示和理解数据成为了一项关键技能。JavaScript 作为前端开发的主要语言,不仅在构建网页方面无可替代,也在数据可视化领域发挥了重要作用。从简单的图表到复杂的交互式展…...

[Redis][哨兵][上]详细讲解
目录 0.前言1.基本概念1.相关名词解释2.主从复制的问题3.人工恢复主节点故障4.哨兵自动恢复主节点故障 0.前言 说明:该章节相关操作不需要记忆,理解流程和原理即可,用的时候能自主查到即可Redis的主从复制模式下,⼀旦主节点由于故…...

如何展开浏览器开发者模式的Fetch/XHR
说明:大多数程序员都用浏览器的F12,开发者模式查看接口,我也不例外。我常用下面这个选项,它会过滤掉掉其他文档、样式请求,只展示访问服务器的接口请求 有次,不知道点了什么,这个菜单消失找不…...

Pydantic 是一个强大的 Python 库
Pydantic 是一个强大的 Python 库,专门用于数据验证和设置管理。以下是对 Pydantic 的详细介绍: 一、主要功能和特点 数据验证: Pydantic 通过 Python 类型注解来定义数据模型,并自动验证输入数据是否符合预定义的类型和结构。提…...

每日OJ题_牛客_NC40链表相加(二)_链表+高精度加法_C++_Java
目录 牛客_NC40链表相加(二)_链表高精度加法 题目解析 C代码 Java代码 牛客_NC40链表相加(二)_链表高精度加法 链表相加(二)_牛客题霸_牛客网 题目解析 模拟⾼精度加法的过程,只不过是在链表中模拟。 C代码 /*…...

Dubbo快速入门(一):分布式与微服务、Dubbo基本概念
文章目录 一、分布式与微服务概念1.大型互联网架构目标2.集群和分布式(1)集群 (Cluster)(2)分布式计算 (Distributed Computing)(3)集群与分布式的关系(4)实践中的应用案例 3.架构演…...

jmeter性能测试---csv数据文件设置
(1)什么时候使用CSV数据文件设置? 当不同的用户,或者同一用户多次循环时,都可以获取到不同的值 (2)使用CSV数据文件设置进行参数化的步骤? 实例: 请求:htt…...

交换基础【计算机网络】
交换基础 1、交换机的工作原理有哪4项操作,地址表如何建立的? 4项基本操作 丢弃 当本端口下的主机访问已知本端口下的主机时丢弃 转发 当某端口下的主机访问已知某端口下的主机时转发 扩散 当某端口下的主机访问未知端口下的主机时要扩散 广播 当某…...

Android12的netd分析
1.文件位置 system/netd/server/目录下的main.cpp和Android.bp 可知编译会生成netd的可执行程序。 2.main函数的流程 int main() {Stopwatch s; 。。。。。。。 // 启动NetlinkManager服务NetlinkManager *nm NetlinkManager::Instance();if (nm nullptr) {ALOGE("Una…...

OpenCV图像文件读写(6)将图像数据写入文件的函数imwrite()的使用
操作系统:ubuntu22.04 OpenCV版本:OpenCV4.9 IDE:Visual Studio Code 编程语言:C11 算法描述 将图像保存到指定的文件中。 函数 imwrite 将图像保存到指定的文件中。图像格式是根据文件名扩展名选择的(参见 cv::imread 获取扩展…...

JVM(HotSpot):方法区(Method Area)
文章目录 一、内存结构图二、方法区定义三、内存溢出问题四、常量池与运行时常量池 一、内存结构图 1.6 方法区详细结构图 1.8方法区详细结构图 1.8后,方法区是JVM内存的一个逻辑结构,真实内存用的本地物理内存。 且字符串常量池从常量池中移入堆中。 …...

JWT的基础与使用
JWT(JSON Web Token) 是一种用于在各方之间传输信息的紧凑、安全的方式,常用于身份验证和授权。它以令牌的形式将用户信息编码后传输,可以确保数据的完整性和安全性。 1.JWT的结构 JWT 是一个基于 JSON 的令牌,由三部…...

处理 VA02修改行项目计划行(SCHEDULE LINES )报错:不可能确定一个消耗帐户
用户修改销售订单行项目 计划行(SCHEDULE LINES) ,从 “CN”改为“CB”时候发生报错提示:不可能确定一个消耗帐户。 Not possible to determine a consumption account Message No. 06138 Diagnosis The system was not able to determine a consumpti…...

【AI基础】pytorch lightning 基础学习
传统pytorch工作流是首先定义模型框架,然后写训练和验证,测试循环代码。训练,验证,测试代码写起来比较繁琐。这里介绍使用pytorch lightning 部署模型,加速模型训练和验证,记录。 准备工作 1 安装pytorch…...

高通量测序技术--组蛋白甲基化修饰、DNA亲和纯化测序,教授(优青)团队指导:从实验设计、结果分析到SCI论文辅助
组蛋白甲基化修饰工具(H3K4me3 ChIP-seq)组蛋白甲基化类型也有很多种,包括赖氨酸甲基化位点H3K4、H3K9、H3K27、H3K36、H3K79和H4K20等。组蛋白H3第4位赖氨酸的甲基化修饰(H3K4)在进化上高度保守,是被研究最多的组蛋白修饰之一。 DNA亲和纯化测序 DNA亲…...

[极客大挑战 2019]RCE ME1
<?php error_reporting(0); if(isset($_GET[code])){$code$_GET[code];if(strlen($code)>40){die("This is too Long.");}if(preg_match("/[A-Za-z0-9]/",$code)){die("NO.");}eval($code); } else{highlight_file(__FILE__); }// ?>…...

计算机毕业设计 中医院问诊系统的设计与实现 Java实战项目 附源码+文档+视频讲解
博主介绍:✌从事软件开发10年之余,专注于Java技术领域、Python人工智能及数据挖掘、小程序项目开发和Android项目开发等。CSDN、掘金、华为云、InfoQ、阿里云等平台优质作者✌ 🍅文末获取源码联系🍅 👇🏻 精…...

FME辅助规划选址
1.需求:新建运动场馆 用地需求:至少1km*2km 找到符合要求的储备地块 2.已有资源:储备用地 现在城市地块储备比较充足,但都是不规则地块 找出可以建大型场馆的地块 3.问题分析 图斑内部可以放下1000*2000的矩形 4.解决思路…...

Unity中的GUIStyle错误:SerializedObject of SerializedProperty has been Disposed.
一运行就循环打印这个报错, 解决办法,每次改参数之后在HIerarchy中手动保存,就会停止循环打印,style中的字体也显示出来了, 或者 直接换个低版本的...

实战篇 | WSL迁移Linux系统到非系统盘(完整实操版)
1. 操作步骤 1.1 确认要导出的Linux系统是否存在(可跳过) # 终端命令 # 查看通过WSL安装的Linux系统列表 wsl -l1.2 导出Linux系统为tar包 # 终端命令 # 格式(过) wsl --export <Distribution Name> <File Name> #…...

增量式编码器实现原理
目录 概述 1 认识增量式编码器 1.1 概述 1.2 增量式编码器的特性 1.3 编码器的硬件 2 增量式编码器实现原理 2.1 编码器信号 2.2 正反转判断 概述 本文主要介绍增量式编码器实现原理,包括增量式编码器的特性,信号特性,以及如何使用编…...

数据集-目标检测系列-口罩检测数据集 mask>> DataBall
数据集-目标检测系列-口罩检测数据集 mask>> DataBall 数据集-目标检测系列-口罩检测数据集 mask 数据量:1W DataBall 助力快速掌握数据集的信息和使用方式,享有百种数据集,持续增加中。 数据项目地址: gitcode: https…...

工作安排 - 华为OD统一考试(E卷)
2024华为OD机试(C卷+D卷)最新题库【超值优惠】Java/Python/C++合集 题目描述 小明每周上班都会拿到自己的工作清单,工作清单内包含n项工作,每项工作都有对应的耗时时长(单位h)和报酬,工作的总报酬为所有已完成工作的报酬之和。那么请你帮小明安排一下工作,保证小明在指定…...

STM32 GPIO - 笔记
1 STM32的GPIO是漏还是源 在 STM32 微控制器中,GPIO(通用输入/输出)引脚既可以配置为漏极开路输出(Open-Drain)模式,也可以配置为推挽输出(Push-Pull)模式。因此,GPIO 引脚既可以作为“漏”(吸电流,Open-Drain),也可以作为“源”(供电流,Push-Pull)来使用。 GP…...

三篇文章速通JavaSE到SpringBoot框架 (中) IO 进程线程 网络编程 XML MySQL JDBC相关概念与演示代码
文章目录 IOfile类的作用I/O的作用将上篇文章综合项目使用IO流升级所需知识点 进程 线程创建线程的三种方式 网络编程网络编程介绍IP地址端口号网络通信协议网络通信协议的分层演示代码 XMLXML的作用是什么?xml特点 注解什么是注解?注解的使用注解的重要…...