当前位置: 首页 > news >正文

手撕C++ list容器:从节点到完整双向链表实现

前言

在C++标准模板库(STL)中,list是一个非常重要的容器,它实现了双向链表的数据结构。本文将深入探讨如何从零开始实现一个完整的list容器,包括节点设计、迭代器实现和核心功能。

整体结构设计

节点设计

首先我们需要定义链表的基本单元——节点:

cpp

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

每个节点包含三个部分:

  • _val: 存储数据值

  • _prev: 指向前一个节点的指针

  • _next: 指向后一个节点的指针

迭代器设计

迭代器是容器的"智能指针",我们使用模板技巧实现iteratorconst_iterator

cpp

template<class T, class Ref, class Ptr>
struct list_iterator
{typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;Node* _node;// 构造函数和运算符重载...
};

这种设计巧妙之处在于通过模板参数RefPtr区分普通迭代器和常量迭代器:

  • iteratorlist_iterator<T, T&, T*>

  • const_iteratorlist_iterator<T, const T&, const T*>

核心实现解析

构造函数与初始化

cpp

list()
{empty_init();
}void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}

这里使用了哨兵节点(头节点)技术,让链表形成环形结构,简化边界条件处理。

拷贝控制:拷贝构造与赋值

cpp

// 拷贝构造函数
list(const list<T>& lt)
{empty_init();for (auto& e : lt){push_back(e);}
}// 赋值运算符(使用Copy-and-Swap技术)
list<T>& operator=(list<T> it)
{swap(it);return *this;
}void swap(list<T>& it)
{std::swap(_head, it._head);std::swap(_size, it._size);
}

赋值运算符采用传值方式,这实现了Copy-and-Swap惯用法:

  1. 参数it通过传值接收(可能是拷贝或移动构造)

  2. 交换当前对象与参数的内容

  3. 返回时参数自动析构,释放旧资源

这种方法异常安全且代码简洁,同时支持拷贝和移动赋值。

迭代器操作

cpp

iterator begin() { return _head->_next; }
iterator end() { return _head; }const_iterator begin() const { return _head->_next; }
const_iterator end() const { return _head; }

迭代器范围是左闭右开区间[begin, end)end()返回头节点,符合STL惯例。

元素访问与修改

cpp

// 插入操作
void insert(iterator pos, const T& x)
{Node* newnode = new Node(x);newnode->_prev = pos._node->_prev;newnode->_next = pos._node;pos._node->_prev->_next = newnode;pos._node->_prev = newnode;_size++;
}// 删除操作
iterator erase(iterator pos)
{assert(pos != end());Node* next_node = pos._node->_next;pos._node->_prev->_next = pos._node->_next;pos._node->_next->_prev = pos._node->_prev;delete pos._node;_size--;return next_node;
}

erase()函数返回下一个有效迭代器,这是为了防止迭代器失效。

容量操作

cpp

size_t size() const { return _size; }
bool empty() const { return _size == 0; }
void clear()
{auto it = begin();while (it != end()){it = erase(it);}
}

维护_size变量可以在O(1)时间内获取元素个数,比遍历链表更高效。

关键技术亮点

  1. 哨兵节点技术:简化边界条件处理,使代码更加健壮

  2. 模板迭代器:使用单一模板实现普通和常量迭代器,减少代码重复

  3. Copy-and-Swap:优雅的赋值运算符实现,保证异常安全

  4. RAII原则:构造函数分配资源,析构函数释放资源

  5. STL兼容:接口设计符合STL标准,可以无缝替换std::list

使用示例

cpp

ym::list<string> myList;
myList.push_back("Hello");
myList.push_back("World");
myList.push_front("C++");// 遍历打印
for (const auto& str : myList) {cout << str << " ";
}
// 输出: C++ Hello World// 链式赋值
ym::list<string> list1, list2, list3;
list1 = list2 = list3; // 支持链式操作
http://www.xdnf.cn/news/1482391.html

相关文章:

  • Ubuntu 22.04.1上安装MySQL 8.0及设置root密码
  • 贪心算法应用:柔性制造系统(FMS)刀具分配问题详解
  • 深度拆解OpenHarmony NFC服务:从开关到卡模拟掌握近场通信技术
  • 雷卯针对米尔MYC-YF13X开发板防雷防静电方案
  • vspere 服务的部署介绍
  • panther X2 armbian24 安装宝塔(bt)面板注意事项
  • 【完整源码+数据集+部署教程】苹果实例分割检测系统源码和数据集:改进yolo11-AggregatedAtt
  • 004-Dephi数据类型
  • c++之基础B(双重循环)(第五课)
  • idf-esp32 | 打印task列表
  • [水果目标检测5]AppleYOLO:基于深度OC-SORT的改进YOLOv8苹果产量估计方法
  • 深入解析达梦数据库核心技术:检查点、redo、undo、MVCC与内存缓存刷盘
  • ​抢占AI搜索新入口:2025年五大专业GEO优化服务商解析
  • Kafka面试精讲 Day 9:零拷贝技术与高性能IO
  • Python+DRVT 从外部调用 Revit:批量创建梁(2)
  • 【PCIe EP 设备入门学习专栏 -- 8.1.1 PCIe EP 接口总结】
  • 解决 Git Push 失败:处理“非快进”与“非相关历史”问题
  • 从零到一构建企业级AI向量服务:AntSK-PyApi深度技术解析
  • 超文本的定义
  • 专项智能练习(教育科学研究的基本方法)
  • 视频动作识别-VideoSwin
  • FPGA学习笔记——SDR SDRAM的读写(调用IP核版)
  • 【LLM】Openai分析大模型出现幻觉的原因
  • 检查权限与申请权限
  • 为什么LIO-SAM的残差项使用对数映射
  • 动态规划题目
  • MotionSound-简单易用的文本转语音工具
  • Linux--命名管道
  • 【大语言模型 44】创造力评估:开放域生成质量测试
  • 【C++/STL】优先级队列,仿函数和反向迭代器