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

STL详解 - list的模拟实现

目录

1. list 的基本结构

1.1 构造函数

2. 迭代器的实现

2.1 构造函数

2.2 自增和自减操作符

2.3 比较操作符

2.4 解引用和箭头操作符

3. list 容器的实现

3.1 构造函数

3.2 拷贝构造

3.3 赋值运算符重载

3.4 析构函数

3.5 迭代器相关函数

3.6 插入和删除函数

3.7 其他函数

4. 测试代码

5. 源码 


1. list 的基本结构

list 是一个双向链表,每个节点包含两个指针,分别指向其前驱节点和后继节点。我们首先定义一个 ListNode 结构来表示链表中的节点。

template<class T>
struct ListNode
{ListNode<T>* _next;   // 指向后继节点ListNode<T>* _prev;   // 指向前驱节点T _data;              // 节点存储的数据// 构造函数ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}
};

1.1 构造函数

构造函数用于初始化节点。如果未传入数据,则使用默认构造函数初始化数据域。前驱和后继指针初始化为 nullptr

ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x)
{}

2. 迭代器的实现

为了方便遍历链表,我们需要定义一个迭代器结构。迭代器需要支持基本的操作,如解引用、自增、自减等。

template<class T, class Ref, class Ptr>
struct ListIterator
{typedef ListNode<T> Node;typedef ListIterator<T, Ref, Ptr> Self;Node* _node;  // 当前节点指针// 构造函数ListIterator(Node* node):_node(node){}// 解引用操作符Ref operator*(){return _node->_data;}// 箭头操作符Ptr operator->(){return &_node->_data;}// 前置自增Self& operator++(){_node = _node->_next;return *this;}// 后置自增Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// 前置自减Self& operator--(){_node = _node->_prev;return *this;}// 后置自减Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}// 比较操作符bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};

2.1 构造函数

迭代器的构造函数接收一个节点指针,并将其存储在 _node 中。

ListIterator(Node* node):_node(node)
{}

2.2 自增和自减操作符

自增和自减操作符分别用于移动迭代器到下一个或前一个节点。

Self& operator++()
{_node = _node->_next;return *this;
}Self operator++(int)
{Self tmp(*this);_node = _node->_next;return tmp;
}Self& operator--()
{_node = _node->_prev;return *this;
}Self operator--(int)
{Self tmp(*this);_node = _node->_prev;return tmp;
}

2.3 比较操作符

比较操作符用于判断两个迭代器是否指向同一个节点。

bool operator!=(const Self& it)
{return _node != it._node;
}bool operator==(const Self& it)
{return _node == it._node;
}

2.4 解引用和箭头操作符

解引用和箭头操作符用于访问节点中的数据。

Ref operator*()
{return _node->_data;
}Ptr operator->()
{return &_node->_data;
}

3. list 容器的实现

list 容器需要管理链表的头节点,并提供插入、删除、遍历等操作。

template<class T>
class list
{
public:typedef ListNode<T> Node;typedef ListIterator<T, T&, T*> iterator;typedef ListIterator<T, const T&, const T*> const_iterator;// 迭代器相关函数iterator begin(){return _head->_next;}iterator end(){return _head;}const_iterator begin() const{return _head->_next;}const_iterator end() const{return _head;}// 构造函数list(){empty_init();}// 拷贝构造list(const list<T>& lt){empty_init();for (auto& e : lt){push_back(e);}}// 赋值运算符重载list<T>& operator=(list<T>& lt){swap(lt);return *this;}// 析构函数~list(){clear();delete _head;_head = nullptr;}// 插入、删除函数void insert(iterator pos, const T& val){Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;}iterator erase(iterator pos){Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);}void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}// 其他函数size_t size() const{return _size;}bool empty(){return _size == 0;}void clear(){iterator it = begin();while (it != end()){it = erase(it);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}private:Node* _head;  // 指向链表头结点的指针size_t _size; // 链表大小// 初始化空链表void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;}
};

3.1 构造函数

  • 初始化一个空链表,确保链表的头节点 _head 被正确创建。

  • 头节点的前驱和后继指针都指向自身,形成一个循环链表。

  • 初始化 _size 为 0,表示链表为空。

// 初始化空链表
void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list()
{empty_init();
}

3.2 拷贝构造

  • 首先调用 empty_init() 初始化一个空链表。

  • 遍历源列表 lt,将每个元素通过 push_back 插入到新列表中。

  • 这种方式确保新列表与源列表具有相同的元素顺序。

list(const list<T>& lt)
{empty_init();for (auto& e : lt){push_back(e);}
}

3.3 赋值运算符重载

赋值运算符重载函数通过交换两个列表的头节点来实现高效赋值。

  • 通过交换两个列表的头节点 _head 和大小 _size,可以高效地完成赋值操作。

  • 这种方式避免了逐个元素的复制,提高了效率。

  • 调用 swap 函数后,原列表的内容被交换到当前列表中。

list<T>& operator=(list<T>& lt)
{swap(lt);return *this;
}

3.4 析构函数

  • 首先调用 clear() 清空列表,释放所有节点。

  • 删除头节点 _head,并将其置为 nullptr,避免悬空指针。

~list()
{clear();delete _head;_head = nullptr;
}

3.5 迭代器相关函数

  • begin() 返回头节点的下一个节点,即第一个有效节点。

  • end() 返回头节点本身,表示迭代的结束位置。

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

3.6 插入和删除函数

insert 函数在指定位置插入一个新节点。

  • 获取当前节点 cur 和其前驱节点 prev

  • 创建一个新节点 newnode,并初始化其数据和指针。

  • 调整 prev cur 的指针,将新节点插入到链表中。

  • 更新链表大小 _size

void insert(iterator pos, const T& val)
{Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++;
}

erase 函数删除指定位置的节点。

  • 获取当前节点 cur、其前驱节点 prev 和后继节点 next

  • 调整 prev next 的指针,绕过当前节点。

  • 删除当前节点 cur,并更新链表大小 _size

  • 返回下一个节点的迭代器,方便链式操作。

iterator erase(iterator pos)
{Node* cur = pos._node;Node* next = cur->_next;Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);// 返回下一位置迭代器
}

3.7 其他函数

push_back pop_back 分别用于尾插和尾删。

  • push_back 调用 insert 在链表末尾插入新节点。

  • pop_back 调用 erase 删除链表末尾的节点。

void push_back(const T& x)
{insert(end(), x);
}void pop_back()
{erase(--end());
}

push_front pop_front 分别用于头插和头删。

  • push_front 调用 insert 在链表头部插入新节点。

  • pop_front 调用 erase 删除链表头部的节点。

void push_front(const T& x)
{insert(begin(), x);
}void pop_front()
{erase(begin());
}

size 函数返回列表中有效节点的数量。

  • 直接返回链表大小 _size,提供高效的大小查询。

size_t size() const
{return _size;
}

empty 函数判断列表是否为空。

  • 检查链表大小 _size 是否为 0,快速判断链表是否为空。

bool empty()
{return _size == 0;
}

clear 函数清空列表。

  • 遍历链表,逐个删除节点,直到链表为空。

void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

swap 函数交换两个列表的头节点。

  • 通过交换头节点和大小,高效地交换两个列表的内容。

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}

4. 测试代码

void test_list1()
{// 基础功能测试list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);// 遍历输出list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;// 添加元素lt.push_back(10);lt.push_back(20);lt.push_front(30);lt.push_front(40);// 使用范围 for 遍历for (auto e : lt){cout << e << " ";}cout << endl;// 删除元素lt.pop_back();lt.pop_back();lt.pop_front();lt.pop_front();// 再次遍历输出for (auto e : lt){cout << e << " ";}cout << endl;
}// 自定义类型测试
struct A
{int _a1;int _a2;A(int a1 = 0, int a2 = 0):_a1(a1),_a2(a2){}
};void test_list2()
{list<A> lt;A aa1(2, 2);A aa2 = {3, 3};lt.push_back(aa1);lt.push_back(aa2);lt.push_back(A(4, 4));lt.push_back({5, 5});lt.push_back({6, 6});// 遍历输出结构体成员list<A>::iterator it = lt.begin();while (it != lt.end()){cout << it->_a1 << ":" << it->_a2 << endl;++it;}
}void PrintList(const list<int>& clt)
{list<int>::const_iterator it = clt.begin();while (it != clt.end()){cout << *it << " ";++it;}cout << endl;
}void test_list3()
{list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);lt.push_back(5);list<int> lt1(lt);PrintList(lt1);
}

5. 源码 

#pragma once // 防止头文件重复包含#include <assert.h> // 断言相关函数
#include <iostream> // 输入输出流
using namespace std;namespace lv // 命名空间lv
{// 链表节点模板类template<class T>struct ListNode{ListNode<T>* _next; // 后继指针ListNode<T>* _prev; // 前驱指针T _data;            // 数据域// 构造函数,初始化节点,默认数据为T类型的默认构造值ListNode(const T& x = T()):_next(nullptr),_prev(nullptr),_data(x){}};// 链表迭代器模板类(泛型设计,支持普通/const迭代器)template<class T, class Ref, class Ptr>struct ListIterator{typedef ListNode<T> Node;          // 节点类型别名typedef ListIterator<T, Ref, Ptr> Self; // 自身类型别名Node* _node; // 当前节点指针// 构造函数,用给定节点指针初始化迭代器ListIterator(Node* node):_node(node){}// 解引用运算符,返回数据引用(Ref可能是T&或const T&)Ref operator*(){return _node->_data;}// 箭头运算符,返回数据指针(Ptr可能是T*或const T*)Ptr operator->(){return &_node->_data;}// 前置++,移动到下一个节点Self& operator++(){_node = _node->_next;return *this;}// 后置++,返回旧值并移动到下一个节点Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}// 前置--,移动到前一个节点Self& operator--(){_node = _node->_prev;return *this;}// 后置--,返回旧值并移动到前一个节点Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}// 比较运算符,判断两个迭代器是否指向不同节点bool operator!=(const Self& it) const{return _node != it._node;}// 比较运算符,判断两个迭代器是否指向相同节点bool operator==(const Self& it) const{return _node == it._node;}};// 双向链表模板类template<class T>class list{typedef ListNode<T> Node; // 节点类型别名public:// 迭代器类型定义typedef ListIterator<T, T&, T*> iterator;             // 可修改迭代器typedef ListIterator<T, const T&, const T*> const_iterator; // 不可修改迭代器// 获取指向第一个元素的迭代器iterator begin(){return _head->_next;}// 获取尾后迭代器(指向头节点)iterator end(){return _head;}// 获取指向第一个元素的const迭代器const_iterator begin() const{return _head->_next;}// 获取尾后const迭代器const_iterator end() const{return _head;}// 初始化空链表(创建头节点并自环)void empty_init(){_head = new Node;      // 创建头节点_head->_next = _head;  // 后继指向自己_head->_prev = _head;  // 前驱指向自己_size = 0;             // 初始化元素个数}// 默认构造函数list(){empty_init();}// 拷贝构造函数(深拷贝)list(const list<T>& lt){empty_init();for (const auto& e : lt) // 遍历源链表元素{push_back(e);       // 将元素逐个插入新链表}}// 交换两个链表的内容void swap(list<T>& lt){std::swap(_head, lt._head); // 交换头指针std::swap(_size, lt._size); // 交换元素个数}// 赋值运算符(通过拷贝交换实现)list<T>& operator=(const list<T>& lt){if (this != &lt) // 防止自赋值{list<T> tmp(lt); // 拷贝构造临时对象swap(tmp);       // 交换当前对象与临时对象}return *this; // 返回当前对象}// 清空链表所有元素void clear(){iterator it = begin();while (it != end()) // 遍历删除所有元素{it = erase(it);}}// 析构函数~list(){clear();        // 清空元素delete _head;   // 释放头节点_head = nullptr;}// 在链表尾部插入元素void push_back(const T& x){insert(end(), x); // 在end()前插入}// 在链表头部插入元素void push_front(const T& x){insert(begin(), x); // 在begin()前插入}// 删除尾部元素void pop_back(){erase(--end()); // 删除end()的前一个元素}// 删除头部元素void pop_front(){erase(begin()); // 删除第一个元素}// 在pos位置前插入元素iterator insert(iterator pos, const T& val){Node* cur = pos._node;  // 当前节点Node* newnode = new Node(val); // 创建新节点Node* prev = cur->_prev; // 前驱节点// 调整指针连接prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;_size++; // 元素计数增加return iterator(newnode); // 返回指向新节点的迭代器}// 删除pos位置的元素iterator erase(iterator pos){assert(pos != end()); // 确保不删除头节点Node* cur = pos._node; // 当前节点Node* next = cur->_next; // 后继节点Node* prev = cur->_prev; // 前驱节点// 调整指针连接prev->_next = next;next->_prev = prev;delete cur;     // 释放节点_size--;        // 元素计数减少return iterator(next); // 返回后继节点的迭代器}// 获取链表元素个数size_t size() const{return _size;}// 判断链表是否为空bool empty(){return _size == 0;}private:Node* _head;  // 头节点指针size_t _size; // 元素个数};
}

相关文章:

  • C++零基础实践教程 指针与内存 类与对象入门 (面向对象基础)
  • 第五节:React Hooks进阶篇-如何用useMemo/useCallback优化性能
  • eSIM RSP(远程SIM配置)架构笔记
  • Spring Boot整合T-IO实现即时通讯
  • 记录第一次面试的经历
  • 游戏盾是什么?重新定义游戏安全边界
  • Sklearn入门之数据预处理preprocessing
  • Node.js 中的 Buffer(缓冲区)
  • esp-idf:多语言--lv_i18n
  • 状态模式详解与真实场景案例(Java实现)
  • 人脸检测-人脸关键点-人脸识别-人脸打卡-haar-hog-cnn-ssd-mtcnn-lbph-eigenface-resnet
  • 如何将 ESP32 快速接入高德、心知、和风天气API 获取天气信息
  • void MainWindow::on_btnOutput_clicked()为什么我在QT里面没有connect,也能触发点击效果
  • 【正点原子STM32MP257连载】第四章 ATK-DLMP257B功能测试——RTC时钟测试 #内部RTC时钟 #外部时钟模块AT8563
  • 运维面试题(十四)
  • 常见编码面试问题
  • 命令模式 (Command Pattern)
  • 问题记录(四)——拦截器“失效”?null 还是“null“?
  • 【iOS】OC高级编程 iOS多线程与内存管理阅读笔记——自动引用计数(一)
  • C++ 核心进阶
  • 网页设计就是做网站优化的吗/苏州网络推广seo服务
  • 做跨国婚恋网站赚钱吗/湖南关键词网络科技有限公司
  • 网站开发与运营方向和企业管理方向/网页模板素材
  • xly000.wordpress.com/seo营销推广公司
  • 做房产抵押网站需要什么手续费/网站主页
  • 如何做自己的网站/制作网站要多少费用