news 2026/6/10 1:10:56

C++ STL list容器深度解析与模拟实现

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
C++ STL list容器深度解析与模拟实现

📚 一、list容器介绍

1.1 基本概念

list是C++标准模板库(STL)中的一个序列容器,底层实现为带头节点的双向循环链表。这种结构使得list在任意位置插入和删除元素都具有很高的效率。

1.2 核心特性

  • 双向访问:可以从前后两个方向遍历

  • 动态内存:每个元素独立分配内存(节点)

  • 迭代器类型:双向迭代器(支持++和--操作)

  • 内存不连续:元素在内存中不是连续存储的

🔧 二、list的基本使用

2.1 构造函数

cpp

#include <list> #include <iostream> void test_construction() { // 1. 默认构造 - 空list std::list<int> list1; // 2. 构造包含n个相同元素的list std::list<int> list2(5, 100); // 5个100 // 3. 范围构造 int arr[] = {1, 2, 3, 4, 5}; std::list<int> list3(arr, arr + 5); // 4. 拷贝构造 std::list<int> list4(list3); // 5. 初始化列表构造(C++11) std::list<int> list5 = {1, 2, 3, 4, 5}; }

2.2 迭代器使用

cpp

void test_iterator() { std::list<int> lst = {1, 2, 3, 4, 5}; // 正向迭代器 std::cout << "正向遍历: "; for (auto it = lst.begin(); it != lst.end(); ++it) { std::cout << *it << " "; } std::cout << std::endl; // 反向迭代器 std::cout << "反向遍历: "; for (auto rit = lst.rbegin(); rit != lst.rend(); ++rit) { std::cout << *rit << " "; } std::cout << std::endl; // C++11范围for循环 std::cout << "范围for遍历: "; for (int val : lst) { std::cout << val << " "; } std::cout << std::endl; }

2.3 容量操作

cpp

void test_capacity() { std::list<int> lst = {1, 2, 3}; std::cout << "是否为空: " << lst.empty() << std::endl; std::cout << "元素个数: " << lst.size() << std::endl; // list没有capacity()方法,因为链表不需要预分配空间 }

2.4 元素访问

cpp

void test_element_access() { std::list<int> lst = {1, 2, 3, 4, 5}; if (!lst.empty()) { std::cout << "第一个元素: " << lst.front() << std::endl; std::cout << "最后一个元素: " << lst.back() << std::endl; } // list不支持随机访问,所以没有operator[]和at()方法 // 要访问第n个元素,需要遍历 }

2.5 修改操作

cpp

void test_modifiers() { std::list<int> lst; // 头部操作 lst.push_front(10); // 头部插入 lst.pop_front(); // 头部删除 // 尾部操作 lst.push_back(20); // 尾部插入 lst.pop_back(); // 尾部删除 // 中间插入 auto it = lst.begin(); ++it; // 移动到第二个位置 lst.insert(it, 30); // 在第二个位置插入30 // 删除 it = lst.begin(); lst.erase(it); // 删除第一个元素 // 清空 lst.clear(); }

⚠️ 三、迭代器失效问题

3.1 错误示例

cpp

// 错误写法:迭代器失效 void wrong_example() { std::list<int> lst = {1, 2, 3, 4, 5}; auto it = lst.begin(); while (it != lst.end()) { lst.erase(it); // 错误!删除后it失效 ++it; // 使用失效的迭代器,未定义行为 } }

3.2 正确写法

cpp

// 正确写法1:使用返回值更新迭代器 void correct_example1() { std::list<int> lst = {1, 2, 3, 4, 5}; auto it = lst.begin(); while (it != lst.end()) { it = lst.erase(it); // erase返回下一个有效迭代器 } } // 正确写法2:后置递增 void correct_example2() { std::list<int> lst = {1, 2, 3, 4, 5}; auto it = lst.begin(); while (it != lst.end()) { lst.erase(it++); // 先传递it,然后递增 } }

注意:对于list,只有指向被删除元素的迭代器会失效,其他迭代器不受影响。

🔨 四、list的模拟实现

4.1 节点结构

cpp

template<class T> struct ListNode { ListNode<T>* _prev; ListNode<T>* _next; T _data; ListNode(const T& val = T()) : _prev(nullptr) , _next(nullptr) , _data(val) {} };

4.2 list类框架

cpp

template<class T> class List { private: ListNode<T>* _head; // 头节点(哨兵节点) public: // 迭代器类 class iterator { private: ListNode<T>* _node; public: iterator(ListNode<T>* node = nullptr) : _node(node) {} // 前置++ iterator& operator++() { _node = _node->_next; return *this; } // 后置++ iterator operator++(int) { iterator temp(*this); _node = _node->_next; return temp; } // 解引用 T& operator*() { return _node->_data; } // 箭头运算符 T* operator->() { return &_node->_data; } // 比较运算符 bool operator!=(const iterator& it) { return _node != it._node; } bool operator==(const iterator& it) { return _node == it._node; } }; // 构造函数 List() { _head = new ListNode<T>(); _head->_prev = _head; _head->_next = _head; } // 析构函数 ~List() { clear(); delete _head; _head = nullptr; } iterator begin() { return iterator(_head->_next); } iterator end() { return iterator(_head); } // 插入、删除等其他方法... };

4.3 反向迭代器实现

cpp

template<class Iterator> class ReverseIterator { private: Iterator _it; public: ReverseIterator(Iterator it) : _it(it) {} // 前置++ ReverseIterator& operator++() { --_it; return *this; } // 后置++ ReverseIterator operator++(int) { ReverseIterator temp(*this); --_it; return temp; } // 解引用(注意反向迭代器的特殊处理) typename Iterator::reference operator*() { Iterator temp = _it; --temp; return *temp; } // 其他必要操作... };

📊 五、list与vector对比

特性vectorlist
底层结构动态数组,连续内存双向链表,非连续内存
随机访问O(1)(支持)O(n)(不支持)
插入/删除尾部O(1),其他位置O(n)任意位置O(1)(已知位置)
内存使用连续,缓存友好,空间利用率高碎片化,缓存不友好,每个元素额外开销
迭代器类型随机访问迭代器双向迭代器
迭代器失效插入/删除可能导致全部迭代器失效只有被删除元素的迭代器失效
适用场景需要随机访问,尾部操作频繁频繁在任意位置插入/删除

🎯 六、选择指南

使用vector的场景:

  • 需要频繁随机访问元素

  • 元素数量变化不大,主要在尾部操作

  • 对缓存性能要求高

  • 需要与其他C函数或库交互(连续内存)

使用list的场景:

  • 频繁在中间位置插入/删除元素

  • 不需要随机访问,主要是顺序访问

  • 元素较大,移动成本高

  • 需要稳定的迭代器(不被插入操作影响)

💡 七、最佳实践

  1. 优先选择vector:除非有明确的理由,否则vector通常是更好的选择

  2. 考虑deque:如果需要头尾高效操作,考虑使用deque

  3. 使用算法库:结合STL算法(如sort、find等)使用

  4. 注意迭代器失效:特别是在循环中修改容器时

总结

list作为STL中的双向链表容器,在特定场景下具有不可替代的优势。理解其底层实现原理、迭代器机制以及与vector的区别,能够帮助我们在实际开发中做出更合理的选择。通过模拟实现list,我们能够更深入地理解STL容器的设计思想,提升C++编程能力。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/30 12:01:55

从Vue到Spring Boot:一位Java全栈开发的面试实录

从Vue到Spring Boot&#xff1a;一位Java全栈开发的面试实录 在一家互联网大厂的面试中&#xff0c;一位28岁的Java全栈开发者李明正在接受一场紧张而富有挑战性的技术面试。他的学历是硕士&#xff0c;拥有5年的工作经验&#xff0c;曾参与多个大型项目的开发与部署。他主要负…

作者头像 李华
网站建设 2026/6/10 13:20:18

适用于开发板的USB Serial驱动Windows下载教程

一文搞定开发板串口通信&#xff1a;Windows下USB转串驱动安装全解析 你有没有过这样的经历&#xff1f;手里的开发板插上电脑&#xff0c;设备管理器里却只显示“未知设备”或一个带黄色感叹号的COM端口。明明线是好的&#xff0c;板子也通电了&#xff0c;但就是连不上串口助…

作者头像 李华
网站建设 2026/6/9 22:23:03

CCS20环境下函数内联优化手把手教程

CCS20环境下函数内联优化实战全解&#xff1a;从原理到工程落地在嵌入式开发的世界里&#xff0c;“快”从来不只是一个目标&#xff0c;而是一种生存法则。特别是在基于TI C2000系列DSP的实时控制系统中&#xff0c;哪怕是一个微秒级的延迟&#xff0c;也可能导致控制环路失稳…

作者头像 李华
网站建设 2026/6/10 15:02:33

KiCad原理图符号标准化:STM32系列器件统一管理指南

如何让STM32在KiCad中“听话”&#xff1f;一套符号规范拯救你的原理图混乱 你有没有遇到过这样的场景&#xff1a; 刚接手一个同事的项目&#xff0c;打开原理图一看——同样是STM32F407&#xff0c;电源引脚位置不一样、调试接口画法五花八门、GPIO命名还夹杂着拼音缩写………

作者头像 李华
网站建设 2026/6/10 11:52:56

STM32 CAN控制器演进:CANFD和CAN的区别全面讲解

STM32上的CAN进化之路&#xff1a;从经典CAN到CANFD的实战解析你有没有遇到过这样的场景&#xff1f;在做ECU通信设计时&#xff0c;明明总线负载已经压到了30%&#xff0c;但CPU却频繁被CAN中断“打爆”&#xff1b;或者OTA升级一个几百KB的固件&#xff0c;传输时间动辄几十秒…

作者头像 李华
网站建设 2026/6/10 11:52:31

大模型推理瓶颈怎么破?NVIDIA TensorRT带来颠覆性解决方案

大模型推理瓶颈怎么破&#xff1f;NVIDIA TensorRT带来颠覆性解决方案 在今天&#xff0c;一个智能客服系统如果响应慢上几十毫秒&#xff0c;用户可能就会选择离开&#xff1b;一辆自动驾驶汽车若因模型延迟未能及时识别障碍物&#xff0c;后果不堪设想。随着大语言模型和视觉…

作者头像 李华