【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亲…...
变量 varablie 声明- Rust 变量 let mut 声明与 C/C++ 变量声明对比分析
一、变量声明设计:let 与 mut 的哲学解析 Rust 采用 let 声明变量并通过 mut 显式标记可变性,这种设计体现了语言的核心哲学。以下是深度解析: 1.1 设计理念剖析 安全优先原则:默认不可变强制开发者明确声明意图 let x 5; …...
深入浅出Asp.Net Core MVC应用开发系列-AspNetCore中的日志记录
ASP.NET Core 是一个跨平台的开源框架,用于在 Windows、macOS 或 Linux 上生成基于云的新式 Web 应用。 ASP.NET Core 中的日志记录 .NET 通过 ILogger API 支持高性能结构化日志记录,以帮助监视应用程序行为和诊断问题。 可以通过配置不同的记录提供程…...
day52 ResNet18 CBAM
在深度学习的旅程中,我们不断探索如何提升模型的性能。今天,我将分享我在 ResNet18 模型中插入 CBAM(Convolutional Block Attention Module)模块,并采用分阶段微调策略的实践过程。通过这个过程,我不仅提升…...
.Net框架,除了EF还有很多很多......
文章目录 1. 引言2. Dapper2.1 概述与设计原理2.2 核心功能与代码示例基本查询多映射查询存储过程调用 2.3 性能优化原理2.4 适用场景 3. NHibernate3.1 概述与架构设计3.2 映射配置示例Fluent映射XML映射 3.3 查询示例HQL查询Criteria APILINQ提供程序 3.4 高级特性3.5 适用场…...
使用分级同态加密防御梯度泄漏
抽象 联邦学习 (FL) 支持跨分布式客户端进行协作模型训练,而无需共享原始数据,这使其成为在互联和自动驾驶汽车 (CAV) 等领域保护隐私的机器学习的一种很有前途的方法。然而,最近的研究表明&…...
工程地质软件市场:发展现状、趋势与策略建议
一、引言 在工程建设领域,准确把握地质条件是确保项目顺利推进和安全运营的关键。工程地质软件作为处理、分析、模拟和展示工程地质数据的重要工具,正发挥着日益重要的作用。它凭借强大的数据处理能力、三维建模功能、空间分析工具和可视化展示手段&…...
TRS收益互换:跨境资本流动的金融创新工具与系统化解决方案
一、TRS收益互换的本质与业务逻辑 (一)概念解析 TRS(Total Return Swap)收益互换是一种金融衍生工具,指交易双方约定在未来一定期限内,基于特定资产或指数的表现进行现金流交换的协议。其核心特征包括&am…...
安卓基础(aar)
重新设置java21的环境,临时设置 $env:JAVA_HOME "D:\Android Studio\jbr" 查看当前环境变量 JAVA_HOME 的值 echo $env:JAVA_HOME 构建ARR文件 ./gradlew :private-lib:assembleRelease 目录是这样的: MyApp/ ├── app/ …...
技术栈RabbitMq的介绍和使用
目录 1. 什么是消息队列?2. 消息队列的优点3. RabbitMQ 消息队列概述4. RabbitMQ 安装5. Exchange 四种类型5.1 direct 精准匹配5.2 fanout 广播5.3 topic 正则匹配 6. RabbitMQ 队列模式6.1 简单队列模式6.2 工作队列模式6.3 发布/订阅模式6.4 路由模式6.5 主题模式…...
基于IDIG-GAN的小样本电机轴承故障诊断
目录 🔍 核心问题 一、IDIG-GAN模型原理 1. 整体架构 2. 核心创新点 (1) 梯度归一化(Gradient Normalization) (2) 判别器梯度间隙正则化(Discriminator Gradient Gap Regularization) (3) 自注意力机制(Self-Attention) 3. 完整损失函数 二…...
