【C++指南】C++ list容器完全解读(二):list模拟实现,底层架构揭秘

news2025/6/3 22:04:21

.

💓 博客主页:倔强的石头的CSDN主页
📝Gitee主页:倔强的石头的gitee主页
⏩ 文章专栏:《C++指南》
期待您的关注
在这里插入图片描述

文章目录

      • 引言
      • 一、链表节点设计:双向链表的基石
        • 1.1 节点类的实现
      • 二、list框架与核心成员函数
        • 2.1 list类的成员变量
        • 2.2 构造函数与初始化
        • 2.3 深拷贝
        • 2.4 赋值运算符重载:传统写法 vs 现代写法
        • 2.4 构造函数重载与冲突解决
      • 三、修改操作:插入与删除
        • 3.1 插入操作`insert`
        • 3.2 删除操作`erase`
      • 四、其他关键函数实现
        • 4.1 容量操作
        • 4.1 交换函数
        • 4.2 自定义swap的高效实现
      • 结语

引言

在上一篇文章【C++指南】STL list容器完全解读(一):从入门到掌握基础操作中,我们深入探讨了list容器的核心特性、使用场景及接口规范。
本文作为系列第二篇,将聚焦于list的底层模拟实现,通过手写双向链表结构,揭示其高效插入删除的底层逻辑。

通过本文,您将掌握:

  • 双向链表的节点设计与内存管理
  • list核心成员函数的实现原理
  • 深拷贝与现代C++优化技巧
  • STL容器设计中的关键思想

一、链表节点设计:双向链表的基石

1.1 节点类的实现

list_node是链表的原子单位,需包含数据域前后指针

template <class T>
struct list_node {
    T data;            // 数据域
    list_node<T>* next; // 后继指针
    list_node<T>* prev; // 前驱指针

    // 构造函数:支持默认值初始化
    list_node(const T& x = T()) 
        : data(x), next(nullptr), prev(nullptr) {}
};

关键点

  • 模板化设计支持任意数据类型
  • 默认构造函数初始化指针为nullptr,避免野指针

二、list框架与核心成员函数

2.1 list类的成员变量
template <class T>
class list {
private:
    typedef list_node<T> Node;
    Node* _head;    // 头节点(哨兵节点)
    size_t _size;   // 元素数量
public:
    // 迭代器声明(下篇详解)
    typedef list_iterator<T, T&, T*> iterator;
    // ... 其他成员函数
};

在这里插入图片描述

核心设计思想

  • 头节点作为哨兵节点,使空链表的begin()end()统一指向_head
  • _size记录元素数量,避免遍历统计
2.2 构造函数与初始化
// 默认构造:创建空链表
list() { empty_init(); }

// 初始化函数:构建头节点闭环
void empty_init() {
    _head = new Node();
    _head->next = _head;
    _head->prev = _head;
    _size = 0;
}

注意事项

  • 头节点的nextprev均指向自身,形成环形结构
2.3 深拷贝

拷贝构造

list(const list<T>& s) {
    empty_init();
    for (auto& i : s) push_back(i); // 深拷贝
}
2.4 赋值运算符重载:传统写法 vs 现代写法

传统写法(显式深拷贝)

list<T>& operator=(const list<T>& s) {  
    if (this != &s) {          // 防止自赋值  
        clear();               // 清空当前链表  
        for (auto& val : s) {  
            push_back(val);    // 逐元素深拷贝  
        }  
    }  
    return *this;  
}  

缺点:代码冗余,需手动处理资源释放与拷贝。

现代写法(资源交换)

list<T>& operator=(list<T> s) {  
    swap(s);    // 传递临时对象,利用拷贝构造完成深拷贝  
    return *this;  
}  

优势

  • 利用拷贝构造函数生成临时对象s,自动完成深拷贝
  • 通过swap交换资源,临时对象s析构时自动释放旧数据

2.4 构造函数重载与冲突解决

1. 多个val值的构造

// 填充构造函数:创建n个值为val的元素  
list(size_t n, const T& val = T()) {  
    empty_init();  
    for (size_t i=0; i<n; ++i) push_back(val);  
}  

// 重载int版本,避免与迭代器范围构造冲突  
list(int n, const T& val = T()) {  
    empty_init();  
    for (int i=0; i<n; ++i) push_back(val);  
}  

2. 初始化列表构造

list(initializer_list<T> il) {  
    empty_init();  
    for (auto& elem : il) push_back(elem);  
}  

3. 迭代器范围构造

template <class InputIterator>  
list(InputIterator first, InputIterator last) {  
    empty_init();  
    while (first != last) {  
        push_back(*first);  
        ++first;  
    }  
}  

为什么需要重载int n版本?
若只有模板版本的迭代器范围构造,当用户调用list<int> lst(5, 1)时:
如下图所示,错误的调用了为迭代器准备的模板函数,就会对int解引用
在这里插入图片描述

  • 编译器优先匹配InputIterator版本(int被推导为迭代器类型)
  • 导致逻辑错误(试图对整数51解引用)
    解决方案:提供int n的重载版本,明确匹配数值构造场景。

三、修改操作:插入与删除

3.1 插入操作insert
iterator insert(iterator pos, const T& val) {
    Node* cur = pos._node;      // 当前节点
    Node* prev = cur->prev;     // 前驱节点
    Node* new_node = new Node(val); // 新节点

    // 链接新节点
    prev->next = new_node;
    new_node->prev = prev;
    new_node->next = cur;
    cur->prev = new_node;

    _size++;
    return iterator(new_node);  // 返回新节点迭代器
}

复用插入实现push_back/push_front

void push_back(const T& x) { insert(end(), x); }
void push_front(const T& x) { insert(begin(), x); }
3.2 删除操作erase
iterator erase(iterator pos) {
    assert(pos != end()); // 禁止删除头节点
    Node* cur = pos._node;
    Node* prev = cur->prev;
    Node* next = cur->next;

    // 跳过被删节点
    prev->next = next;
    next->prev = prev;
    delete cur;

    _size--;
    return iterator(next); // 返回下一节点迭代器
}

复用删除实现pop_back/pop_front

void pop_back() { erase(--end()); }
void pop_front() { erase(begin()); }

四、其他关键函数实现

4.1 容量操作
// 清空链表(保留头节点)
void clear() {
    iterator it = begin();
    while (it != end()) it = erase(it);
}

bool empty()//判空
{
	return _size == 0;
}

size_t size()//获取链表元素个数
{
	return _size;
}
4.1 交换函数

STL的std::swap通过三次拷贝完成交换:

template <class T>  
void swap(T& a, T& b) {  
    T tmp(a);  
    a = b;  
    b = tmp;  
}  

问题

  • 对链表而言,逐节点拷贝效率极低(时间复杂度O(n))
4.2 自定义swap的高效实现

类内swap:直接交换头指针与_size

void swap(list<T>& other) {  
    std::swap(_head, other._head); // O(1)交换  
    std::swap(_size, other._size);  
}  

全局swap适配:确保ADL正确调用

template <class T>  
void swap(list<T>& a, list<T>& b) {  
    a.swap(b); // 调用类内swap  
}  

为何需要全局swap?

  • 若用户调用swap(lst1, lst2),编译器优先查找参数关联的命名空间
  • 全局swap确保调用自定义实现,而非低效的std::swap

结语

本文从双向链表的节点设计出发,逐步实现了list的核心功能,揭示了STL容器设计中的内存管理与接口复用思想。
在下一篇文章中,我们将深入探讨迭代器的封装与类型萃取技术

下篇预告:《【C++指南】C++ list容器完全解读(三):list迭代器的实现与优化》—— 揭秘STL迭代器如何实现“透明访问”与高效遍历!

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

[神经网络]使用olivettiface数据集进行训练并优化,观察对比loss结果

结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 搭建的神经网络&#xff0c;使用olivettiface数据集进行训练&#xff0c;结合归一化和正则化来优化网络模型结构&#xff0c;观察对比loss结果 from sklearn.datasets import fetch_olivetti_faces #倒入数…

华院计算出席信创论坛,分享AI教育创新实践并与燧原科技共同推出教育一体机

5月21日&#xff0c;信创论坛于上海漕河泾会议中心举办。本次论坛以“聚力融合&#xff0c;繁荣生态”为主题&#xff0c;话题聚焦工业制造、交通运输、金融、教育、医疗等领域。华院计算技术&#xff08;上海&#xff09;股份有限公司&#xff08;以下简称“华院计算”&#x…

华为OD机试真题——会议接待 /代表团坐车(2025A卷:200分)Java/python/JavaScript/C++/C语言/GO六种最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《会议…

LabVIEW Val (Sgnl) 属性

在 LabVIEW 事件驱动架构中&#xff0c;Val (Sgnl) 属性&#xff08;Value (Signaling)&#xff09;是实现编程触发与用户交互行为一致性的关键技术。与普通 Value 属性不同&#xff0c;Val (Sgnl) 在修改控件值的同时强制生成值改变事件&#xff0c;确保程序逻辑与 UI 交互保持…

STM32G4 电机外设篇(三) TIM1 发波 和 ADC COMP DAC级联

目录 一、STM32G4 电机外设篇&#xff08;三&#xff09; TIM1 发波 和 ADC COMP DAC级联1 TIM1 高级定时器发波1.1 stm32cubemx配置 2 TIM1 ADC COMP DAC级联2.1 stm32cubemx配置 附学习参考网址欢迎大家有问题评论交流 (* ^ ω ^) 一、STM32G4 电机外设篇&#xff08;三&…

DAY 35 超大力王爱学Python

知识点回顾&#xff1a; 三种不同的模型可视化方法&#xff1a;推荐torchinfo打印summary权重分布可视化进度条功能&#xff1a;手动和自动写法&#xff0c;让打印结果更加美观推理的写法&#xff1a;评估模式 作业&#xff1a;调整模型定义时的超参数&#xff0c;对比下效果。…

【数据结构】图的存储(十字链表)

弧节点 tailvex数据域&#xff1a;存储弧尾一端顶点在顺序表中的位置下标&#xff1b;headvex 数据域&#xff1a;存储弧头一端顶点在顺序表中的位置下标&#xff1b;hlink 指针域&#xff1a;指向下一个以当前顶点作为弧头的弧&#xff1b;tlink 指针域&#xff1a;指向下一个…

Redis最佳实践——秒杀系统设计详解

基于Redis的高并发秒杀系统设计&#xff08;十万级QPS&#xff09; 一、秒杀系统核心挑战 瞬时流量洪峰&#xff1a;100万 QPS请求冲击库存超卖风险&#xff1a;精准扣减防止超卖系统高可用性&#xff1a;99.99%服务可用性要求数据强一致性&#xff1a;库存/订单/支付状态同步…

STM32软件spi和硬件spi

核心观点 本文主要介绍了SPI通信的两种实现方式&#xff1a;软件SPI和硬件SPI。详细阐述了SPI通信协议的基本概念、硬件电路连接方式、移位示意图、时序基本单元以及四种工作模式。同时&#xff0c;对W25Q64模块进行了详细介绍&#xff0c;包括其硬件电路、框图以及操作注意事…

深度刨析树结构(从入门到入土讲解AVL树及红黑树的奥秘)

目录 树的表示 二叉树的概念及结构&#xff08;重点学习&#xff09; 概念 &#xff1a; 特点&#xff1a; 树与非树 特殊的二叉树 二叉树的性质(重点) 二叉树的存储结构 堆的概念及结构 建堆方式&#xff1a; 向下调整算法 向上调整算法 建堆第一步初始化 建…

【Linux】shell的条件判断

目录 一.使用逻辑运算符判定命令执行结果 二.条件判断方法 三.判断表达式 3.1文件判断表达式 3.2字符串测试表达式 3.3整数测试表达式 3.4逻辑操作符 一.使用逻辑运算符判定命令执行结果 && 在命令执行后如果没有任何报错时会执行符号后面的动作|| 在命令执行后…

第九天:java注解

注解 1 什么是注解&#xff08;Annotation&#xff09; public class Test01 extends Object{//Override重写的注解Overridepublic String toString() {return "Test01{}";} }2 内置注解 2.1 Override Override重写的注解 Override public String toString() {ret…

十一、【核心功能篇】测试用例管理:设计用例新增编辑界面

【核心功能篇】测试用例管理&#xff1a;设计用例新增&编辑界面 前言准备工作第一步&#xff1a;创建测试用例相关的 API 服务 (src/api/testcase.ts)第二步&#xff1a;创建测试用例编辑页面组件 (src/views/testcase/TestCaseEditView.vue)第三步&#xff1a;配置测试用例…

Spring是如何实现属性占位符解析

Spring属性占位符解析 核心实现思路1️⃣ 定义占位符处理器类2️⃣ 处理 BeanDefinition 中的属性3️⃣ 替换具体的占位符4️⃣ 加载配置文件5️⃣ Getter / Setter 方法 源码见&#xff1a;mini-spring 在使用 Spring 框架开发过程中&#xff0c;为了实现配置的灵活性&#xf…

DDR4读写压力测试

1.1测试环境 1.1.1整体环境介绍 板卡&#xff1a; pcie-403板卡 主控芯片&#xff1a; Xilinx xcvu13p-fhgb2104-2 调试软件&#xff1a; Vivado 2018.3 代码环境&#xff1a; Vscode utf-8 测试工程&#xff1a; pcie403_user_top 1.1.2硬件介绍 UD PCIe-403…

编写测试用例

测试用例&#xff08;Test Case&#xff09;是用于测试系统的要素集合 目录 编写测试用例作用 编写测试用例要包含七大元素 测试用例的设计方法 1、等价类法 2、边界值法 3、正交表法 4、判定表法 5、错误推测法 6、场景法 编写测试用例作用 1、确保功能全面覆盖…

每日Prompt:隐形人

提示词 黑色棒球帽&#xff0c;白色抹胸、粉色低腰短裙、白色襪子&#xff0c;黑色鞋子&#xff0c;粉紅色背包&#xff0c;衣服悬浮在空中呈现动态姿势&#xff0c;虚幻引擎渲染风格&#xff0c;高清晰游戏CG质感&#xff0c;户外山林背景&#xff0c;画面聚焦在漂浮的衣服上…

TensorFlow深度学习实战(19)——受限玻尔兹曼机

TensorFlow深度学习实战&#xff08;19&#xff09;——受限玻尔兹曼机 0. 前言1. 受限玻尔兹曼机1.1 受限玻尔兹曼机架构1.2 受限玻尔兹曼机的数学原理 2. 使用受限玻尔兹曼机重建图像3. 深度信念网络小结系列链接 0. 前言 受限玻尔兹曼机 (Restricted Boltzmann Machine, RB…

告别手动绘图!基于AI的Smart Mermaid自动可视化图表工具搭建与使用指南

以下是对Smart Mermaid的简单介绍&#xff1a; 一款基于 AI 技术的 Web 应用程序&#xff0c;可将文本内容智能转换为 Mermaid 格式的代码&#xff0c;并将其渲染成可视化图表可以智能制作流程图、序列图、甘特图、状态图等等&#xff0c;并且支持在线调整、图片导出可以Docke…

【Oracle】安装单实例

个人主页&#xff1a;Guiat 归属专栏&#xff1a;Oracle 文章目录 1. 安装前的准备工作1.1 硬件和系统要求1.2 检查系统环境1.3 下载Oracle软件 2. 系统配置2.1 创建Oracle用户和组2.2 配置内核参数2.3 配置用户资源限制2.4 安装必要的软件包 3. 目录结构和环境变量3.1 创建Ora…