【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),其中包括顺序容器(如 vectordeque)和关联容器(如 mapset)。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)交换当前 listx 中的元素
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 容器,解锁高效编程的秘密武器的内容啦,各位大佬有什么问题欢迎在评论区指正,或者私信我也是可以的啦,您的支持是我创作的最大动力!❤️

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/433140.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【Linux】Linux 的 权限

一、 Linux 权限的概念 Linux下有两种用户&#xff1a;超级用户&#xff08;root&#xff09;、普通用户。 超级用户&#xff1a;可以在 linux 系统下做任何事情&#xff0c;不受限制普通用户&#xff1a;在 linux 下做有限的事情。超级用户的命令提示符是“#”&#xff0c;普…

初学51单片机之I2C总线与E2PROM

首先先推荐B站的I2C相关的视频I2C入门第一节-I2C的基本工作原理_哔哩哔哩_bilibili 看完视频估计就大概知道怎么操作I2C了&#xff0c;他的LCD1602讲的也很不错&#xff0c;把数据建立tsp和数据保持thd&#xff0c;比喻成拍照时候的摆pose和按快门两个过程&#xff0c;感觉还是…

Mysql之索引优化

指定索引 当一个字段上既有单列索引&#xff0c;又有复合索引时&#xff0c;我们可以通过以下的SQL提示来要求该SQL语句执行时采用哪个索引&#xff1a; use index(索引名称)&#xff1a;建议使用该索引&#xff0c;只是建议&#xff0c;底层mysql会根据实际效率来考虑是否使用…

使用豆包MarsCode 实现高可用扫描工具

以下是「 豆包MarsCode 体验官」优秀文章&#xff0c;作者郝同学测开笔记。 前言&#xfeff; 最近接触K8s&#xff0c;了解到K8s提供了非常方便的实现高可用的能力&#xff0c;再加上掘金推出「豆包MarsCode初体验」征文活动&#xff0c;所以打算使用豆包 MarsCode IDE来实现…

LeetCode(Python)-贪心算法

文章目录 买卖股票的最佳时机问题穷举解法贪心解法 物流站的选址&#xff08;一&#xff09;穷举算法贪心算法 物流站的选址&#xff08;二&#xff09;回合制游戏快速包装 买卖股票的最佳时机问题 给定一个数组&#xff0c;它的第 i 个元素是一支给定股票第 i 天的价格。如果你…

Qemu开发ARM篇-5、buildroot制作根文件系统并挂载启动

文章目录 1、 buildroot源码获取2、buildroot配置3、buildroot编译4、挂载根文件系统 在上一篇 Qemu开发ARM篇-4、kernel交叉编译运行演示中&#xff0c;我们编译了kernel&#xff0c;并在qemu上进行了运行&#xff0c;但到最后&#xff0c;在挂载根文件系统时候&#xff0c;挂…

python之装饰器、迭代器、生成器

装饰器 什么是装饰器&#xff1f; 用来装饰其他函数&#xff0c;即为其他函数添加特定功能的函数。 装饰器的两个基本原则&#xff1a; 装饰器不能修改被装饰函数的源码 装饰器不能修改被装饰函数的调用方式 什么是可迭代对象&#xff1f; 在python的任意对象中&#xff…

C# DotNetty客户端

1. 引入DotNetty包 我用的开发工具是VS2022&#xff0c;不同工具引入可能会有差异 工具——>NuGet包管理器——>管理解决方案的NuGet程序包 搜索DotNetty 2.新建EchoClientHandler.cs类 用于接收服务器返回数据 public class EchoClientHandler : SimpleChannelIn…

【AD那些事 10 】焊盘如何修改为自己想要的形状!!!!! 焊盘设计规则如何更改??????

左侧为修改前焊盘原图 右侧为修改后焊盘图 ——————————————————————————————————————————— 目录 修改焊盘内侧的大小 修改焊盘外侧的大小 更改焊盘设计规则 ——————————————————————————…

Pencils Protocol 即将登录各大 CEX,依旧看好 $DAPP

近期&#xff0c;Scroll生态头部DeFi协议Pencils Protocol迎来了系列重磅市场进展。自9月18日开始&#xff0c;$DAPP通证分别在Tonkensoft、Bounce以及Coresky等平台陆续开启了IDO&#xff0c;并且在短期内售罄。同时在通证售卖完成后&#xff0c;DAPP 通证又在9月27日陆续登录…

RUST语言的初印象-从一个模拟登陆谈起-slint+reqwest+aes

本文就一个做了三四天的小程序讲第一次学用RUST的感受&#xff0c;内附代码。 了角语言 从一些渠道听说了R&#xff0c;这个字母挺魔性&#xff0c;那个文章说C和R的团体已经上升到了宗教崇拜的高度&#xff0c;然后&#xff0c;我觉得必 有过人之处&#xff0c;大约10年没碰…

通用运维基础

一 网络基础 知识点:网络交换1.1 VLAN1.2VxLAN2.网络路由3.网络常用命令目标:1. 了解网络的基本概念 2. 掌握常用的网络排错命令 1、网络交换 1.1 网络虚拟化 什么是网络虚拟化 网络虚拟化是指虚拟网络节点之间的连接并不使用物理线缆连接,而是依靠特定的虚拟化链路相连…

TCP的第三次握手没有回复,会出现哪些问题现象

从三次握手的一开始来讲&#xff0c;刚开始客户端和服务器都处于close状态 这里不能是2次握手的原因就在于&#xff0c;服务器端即女孩子&#xff0c;无法确认客户端即男孩子&#xff0c;是否已经收到了&#xff0c;我也愿意建立连接即我也爱你&#xff0c;这一条最终确认的信息…

软件对比 | 历史气象数据哪里找?

中国气象网和羲和能源气象大数据平台两个平台当然没有“绝对”哪个好的说法&#xff0c;各自特点都在下图进行总结&#xff0c;到底用哪个还是根据自己需求自己抉择。 希望可以帮助到大家~

DK5V100R15ST1直插TO220F,12V 4A两个引脚同步整流芯片

高性能两个引脚同步整流芯片 DK5V100R15ST1产品 概述DK5V100R15ST1是一款简单高效率的同步整流芯片&#xff0c;只有A&#xff0c;K两个引脚&#xff0c;分别对应肖特基二极管PN管脚。芯片内部集成了100V功率NMOS管&#xff0c;可以大幅降低二极管导通损耗&#xff0c;提高整机…

.NET 6 中,使用 ActionFilterAttribute 实现 AOP(面向切面编程)

AOP概述&#xff1a;AOP&#xff08;面向切面编程&#xff09;是一种编程规范的风格&#xff0c;通过横切的思想&#xff0c;将系统功能和业务功能分离开&#xff0c;以提高代码的可维护性和清晰度。 系统功能模块&#xff1a; 1、缓存模块&#xff1a; 作用&#xff1a;提高…

拥塞控制算法的 rtt 公平性

我强调过&#xff0c;拥塞控制的核心在公平可用性&#xff0c;公平性由 buffer 动力学保证&#xff0c;而 buffer 动力学有两种表现形式&#xff1a; buffer 占比决定带宽占比&#xff0c;以 aimd 为例&#xff1b;带宽越小&#xff0c;buffer 挤兑加速比越大&#xff0c;以 b…

如何选择高品质SD卡

如何选择高品质SD卡 SD卡&#xff08;Secure Digital Memory Card&#xff09;是一种广泛使用的存储器件&#xff0c;因其快速的数据传输速度、可热插拔的特性以及较大的存储容量&#xff0c;广泛应用于各种场景&#xff0c;例如在便携式设备如智能手机、平板电脑、运动相机等…

作者分享|eDNA研究梯级水坝对浮游植物和浮游动物群落变化的影响

研究梯级水坝的影响对于了解和减轻其对环境的负面影响至关重要&#xff0c;浮游植物和浮游动物群落都对梯级水坝引起的变化尤为敏感。凌恩客户重庆师范大学生命科学学院水生态健康与环境安全实验室沈彦君课题组&#xff0c;通过eDNA宏条码技术对梯级水坝河道的浮游植物和浮游动…

uniapp实现在表单中展示多个选项,并且用户可以选择其中的一个或多个选项

前言 uni-data-checkbox是uni-app的一个组件,用于在表单中展示多个选项,并且用户可以选择其中的一个或多个选项。该组件可以通过设置不同的参数来控制选项的样式、布局和行为。 提示:以下是本篇文章正文内容,下面案例可供参考 uni-data-checkbox组件具有以下特点:: 1、跨…