这篇小编将以SGI版的list为例进行模拟实现。

目录

一.主体框架

二.具体实现

(一).节点node

(二).主体list(封装,函数)

①对节点封装

②构造函数、拷贝构造函数

③析构函数

④头插头删push/pop_front、尾插尾删push/pop_back

⑤定向插入insert

⑥定向删除erase

⑦赋值

⑧长度size与扩容resize

⑨swap

⑩其他函数empty/front/back/begin/end/rbegin/rend

(三).迭代器iterator

(四).反向迭代器reverse_iterator

三.完整代码


一.主体框架

这里小编做了一张图,它足以表明list的大致框架:

先有框架再来一点点具体去了解~

二.具体实现

(一).节点node

对于节点node而言,我们可以按照c语言中的双向链表节点结构进行创建。

首先node设为一种类,由于list属于泛型编程,我们不知道链表数据类型,因此使用template<class T>将node变成泛型类。内部参数分别是node* next,node* prev,T value。

大致结构如下:

template<class T>
class list_node
{
public:list_node(T _value = T())//构造函数,使用匿名对象作为缺省:value(_value),next(nullptr),prev(nullptr){}T value;//节点数据list_node* next;//下一个节点list_node* prev;//上一个节点
};

通过代码我们可以发现,在结构上list_node与c语言的结构体链表节点基本无异。

但是C++特性赋予了链表节点泛型编程的强大功能。

(二).主体list(封装,函数)

当我们使用list模板时,不可能自己一个个创建节点,再将之连接,否则就与c语言无异了。

因此,需要对节点进一步封装并加上与之配套的函数。由此我们需要创建list类。

①对节点封装

其实这很简单,我们只需要在list类内部定义一个node*成员变量作为哨兵位头节点,即node* head。换句话说,其实list内部成员就是链表的头节点。

当我们需要操作链表时,从头节点开始即可。

②构造函数、拷贝构造函数

默认情况下,我们只需要一个头节点即可,即next、prev都指向自己。

如图所示:

typedef list_node<T> Node;
void list_init()//成员函数中不免要使用list初始化,不妨专门定义一个这样的函数
{head = new Node;head->next = head;head->prev = head;
}
List()
{list_init();
}

当然list构造函数也应该支持按数量和类型构造

list(int n, const T& value = T())
{list_init();for (int i = 0; i < n; ++i)push_back(value);//后文会有实现
}

除此之外我们发现,官方list还支持按照迭代器的方式构造

在这里我们可以专门定义一个模板用于表示迭代器类型,这样不管何种迭代器都可以进行构造:

template<class InputIterator>
List(InputIterator start, InputIterator end)
{list_init();while (start != end){push_back(*start);start++;}
}

需要注意,list的拷贝构造函数应该是深拷贝

List(const Node& list)
{Node tmp(list.begin(), list.end());//使用上述构造函数swap(tmp);//专门定义一个函数用于交换两个list头节点即可
}

③析构函数

对于析构函数而言,最重要的是清空链表节点,以防内存泄漏。

~list()
{Node* cur = head->next;while (cur != head){head->next = cur->next;delete cur;cur = head->next;}head->next = head->prev = head;//将链表闭环delete head;head = nullptr;
}

④头插头删push/pop_front、尾插尾删push/pop_back

这里的实现思路与普通的带头双向链表无异,具体可以参考这篇博客:带头双向循环链表基础知识归纳

⑤定向插入insert

首先看一下官方定义:

我们可知,insert是在选择的迭代器前插入数据,且返回值是插入的第一个元素的迭代器。

那么实现方式就出来了,在pos位置之前插入一个元素即可:

iterator insert(iterator pos, const T& val)
{Node* pNewNode = new Node(val);Node* pCur = pos.node;pNewNode->prev = pCur->prev;pNewNode->next = pCur;pNewNode->prev->next = pNewNode;pCur->prev = pNewNode;return iterator(pNewNode);
}

当然,批量化插入也可以实现,只需要按所需插入数量while循环调用insert即可,注意返回值是第一次插入的数据的迭代器。

对于参数为迭代器的insert函数只需要从头到尾遍历一遍参数迭代器,将之插入pos之前即可。

⑥定向删除erase

 从官方定义中我们知道,erase会删除position所代表的迭代器或从first迭代器开始直到last(last不删),返回值是被删除元素的下一个位置的迭代器。

实现方式很简单,我们只需要把pos之前与之后的节点相连即可:

iterator erase(iterator pos)
{Node* pDel = pos.node;Node* pRet = pDel->next;pDel->prev->next = pDel->next;pDel->next->prev = pDel->prev;delete pDel;return iterator(pRet);
}

对于参数为迭代器的同理,只需要把first之前节点与last节点相连,清空原先first与last之间节点,返回last节点。

⑦赋值

对于赋值而言,本质是深拷贝,因此,直接在传参时调用拷贝构造深拷贝即可。

Node& operator=(Node l)//参数是实参的拷贝构造
{swap(l);//调用list成员函数swapreturn *this;
}

⑧长度size与扩容resize

size的实现与c中一样,遍历链表即可。

size_t size()const
{Node* cur = head->next;size_t count = 0;while (cur != head){count++;cur = cur->next;}return count;
}

扩容需要判断是增容还是缩容。增容push_back,缩容pop_back。

void resize(size_t newsize, const T& data = T())
{size_t oldsize = size();//成员函数sizeif (newsize <= oldsize)//缩容{while (newsize < oldsize){pop_back();oldsize--;}}else//扩容{while (oldsize < newsize){push_back(data);oldsize++;}}
}

⑨swap

对于swap函数而言直接交换两个list的头节点指针即可。

void swap(Node& list)
{std::swap(head, list.head);
}

⑩其他函数empty/front/back/begin/end/rbegin/rend

这一部分比较简单,直接看下文完整代码即可。

(三).迭代器iterator

对于正向迭代器而言,其使用了类模板而不是单纯使用一个指针实现。

具体原因可以参考这篇博客: 为什么STL中List的实现其迭代器iterator是类模板?

当我们在实现迭代器的时候,需要时刻记得它在表面上看是一个链表节点的指针。因此,其内部一定有一个指向链表节点的指针,还需要支持所有与指针有关的操作:

template<class T, class Ref, class Ptr>
class list_iterator {typedef list_node<T> Node;typedef list_iterator<T, Ref, Ptr> Self;typedef list_iterator<T, T&, T*> iterator;typedef list_iterator<T, const T&, const T*> const_iterator;public://用于reverse_iteratortypedef Ref Ref;typedef Ptr Ptr;typedef T T;public:list_iterator(Node* _node):node(_node){}//支持const_iterator接收iteratorlist_iterator(const iterator& it): node(it.node){}Ref operator*() {return node->value;}Ptr operator->() {return &(node->value);}bool operator==(const Self& it) const{return node == it.node;}bool operator!=(const Self& it) const{return node != it.node;}Self operator++() {node = node->next;return *this;}Self operator++(int) {iterator tmp(node);node = node->next;return tmp;}Self operator--() {node = node->prev;return *this;}Self operator--(int) {iterator tmp(node);node = node->prev;return tmp;}Node* node;
};

当然,小编在这里利用拷贝构造函数实现了由普通iterator隐式类型转化成const_iterator。

具体原理在此:STL中list如何实现普通迭代器隐式类型转换成const迭代器

(四).反向迭代器reverse_iterator

SGI版的list对反向迭代器的实现非常巧妙,所谓反向迭代器就是正向的倒序,那么我们可以在内部封装一个正向迭代器,反向++就是正向--,反向--就是正向++。

template<class Iterator>
class _reverse_iterator
{//typename用于明确这是类型名,否则编译器会将之与类成员混淆,//因为类成员也使用::作用域符访问typedef typename Iterator::Ref Ref;typedef typename Iterator::Ptr Ptr;typedef typename Iterator::T T;typedef _reverse_iterator<Iterator> Self;typedef list_iterator<T, T&, T*> iterator;typedef _reverse_iterator<iterator> reverse_iterator;
public:_reverse_iterator(const iterator& it):_it(it){}//支持const_reverse_iterator接收reverse_iterator_reverse_iterator(const reverse_iterator& rit): _it(rit._it){}Self operator++(){_it--;return *this;}Self operator--(){_it++;return *this;}Ref operator*() {iterator tmp = _it;tmp--;return *tmp;}Ref operator->(){iterator tmp = _it;tmp--;return &(*tmp);}bool operator==(const Self& rit)const{return _it != rit._it;}bool operator!=(const Self& rit)const{return _it != rit._it;}
private:Iterator _it;//内部封装了一个正向迭代器
};

当然,const_reverse_iterator接收普通反向迭代器也是通过拷贝构造函数实现,其参数是一个普通反向迭代器。 本质还是调用正向迭代器的拷贝构造。

三.完整代码

看在小编如此用心的份上,点赞支持一下吧

C++语法——超详细模拟实现list源代码相关推荐

  1. Scala的基础语法(超详细版)

    Scala的基础语法 文章目录 Scala的基础语法 1.声明值和变量 2.数据类型 3.算术和操作符重载 4.控制结构语句 4.1条件分支语句 4.2循环语句 5.方法与函数 5.1方法 5.2 函 ...

  2. spring boot整合freemarker及freemarker基础语法超详细讲解

    采用模板+数据=HTML 实现页面的静态化. 也就是服务端的页面静态化技术. JSP/Freemarker/Thymeleaf是常见的模板引擎. 引依赖 okhttp与HttpClient一样的作用, ...

  3. 【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码

    问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界 ...

  4. python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上

    Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...

  5. Pyqt搭建YOLOV3目标检测界面(超详细+源代码)

    Pyqt搭建YOLOV3目标检测界面(超详细+源代码) 2022.5.25更新 2021.11.23 更新 2021.11.22 更新 实现效果如下所示,可以检测图片.视频以及摄像头实时检测. 0.准 ...

  6. MySQL数据库快速入门到精通(超详细保姆级,建议收藏)这可能是目前最适合你的教程,从基础语法到实例演示。

    前言 此文章旨在为需要掌握快速开发和复习MySQL的同学所准备,您完全可以把此文章当作参考文档来使用,本文将尽量精简,使您快速的理解和掌握语法. 关于MySQL MySQL是一个关系型数据库管理系统, ...

  7. JAVA使用HttpClient模拟登录正方教务系统,爬取学籍信息和课程表成绩等,超详细登录分析和代码注解

    目录 前言 分析 代码实现 第一次GET POST登录 第二次Get 第三次GET 第四次GET 第五次GET 测试 完整代码 前言 最近在做一个APP,需要获取我们学校--武汉纺织大学皇家停水断电断 ...

  8. php - 超详细将 pdf 文档格式转换为图片格式,将 offce pdf 演示文稿转成图像 png / jpg(小白一看就懂的详细教程,附带完整示例源代码)

    效果图 其他教程都有点乱而且有bug,本文站在新手小白的角度比较靠谱,超详细的完整流程及详细注释代码. 本文实现了 php 将 pdf 文档转为图片(png / jpg),完整流程及示例源代码, 你可 ...

  9. react的超详细讲解

    create-react-app 项目目录 在HTML中使用react 1 2 3基础 React的注意事项 模拟的React 和 render React组件 函数组件 类组件 React 的数据源 ...

最新文章

  1. 二维绘图引擎:圆、半圆、弧线绘制
  2. 【重复制造精讲】4、计划初识
  3. SAP 移动类型详解
  4. office 自动编号系列碰到问题小解
  5. 十大经典数据挖掘算法:SVM
  6. IdentityServer4与ocelot实现认证与客户端统一入口
  7. 基于单链表的生产者消费者问题
  8. xgboost实例_XGBoost超详细推导,终于有人讲明白了!
  9. 云原生人物志|华为云CTO张宇昕:云原生已经进入深水区
  10. locust mysql_locust性能压测连接mysql,随机取出班级,绑定学生
  11. 学习Spring Boot:(十七)Spring Boot 中使用 Redis
  12. 《数字逻辑设计与计算机组成》一 第2章 2.1 简介
  13. 分数的计算机应用教案,计算机应用实训实验
  14. xcode6 使用MJRefresh
  15. BZOJ——T 1612: [Usaco2008 Jan]Cow Contest奶牛的比赛
  16. 趋势线与123法则应用图解,很多大佬偷偷在用
  17. 儿童节html5小游戏,六一儿童节游戏大全,六一儿童节游戏有哪些
  18. html如何设置顶部,css中文字如何设置在顶部
  19. 无人驾驶学习介绍和感悟
  20. BLE MESH组网(一)简介和基本概念

热门文章

  1. oracle pns配置,Fanuc机器人IO配置和UIUO配置.doc
  2. 实验9、键盘扫描及数码管显示实验
  3. linux 大牛博客,大牛
  4. eclipse 项目有红叉(解决方法)
  5. 二阶龙格库塔公式推导_连续系统数值仿真方法——龙格库塔法
  6. 阿里云AliGenie开发天猫语音功能-入门篇
  7. Nifi博客之一:概述贴
  8. 【学习】如何制作手机端html模板(REM的实际应用)
  9. 模拟退火算法 python实现旅行家问题
  10. 关于INS-30131