这篇小编将以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. Revit的Enscape基本培训(2021) Enscape Essential Training for Revit (2021)
  2. Python生物信息学③提取差异基因
  3. C++中的istringstream 的用法
  4. linux鼠标触摸屏应用程序,在Ubuntu环境下实现插入鼠标自动关闭触摸板
  5. 游戏设计模式——面向数据编程思想
  6. tensorflow 之 最近用到的几个小操作tf.reshape,tf.convert_to_tensor,tf.where
  7. 【转】深入理解Windows消息机制
  8. Spring中使用XML方式导入Spring配置文件,Boot中使用全注解导入Spring配置
  9. 程序员的算法课(10)-字符串排序算法实例(纯代码)
  10. 《『若水新闻』客户端开发教程》——11.代码编码(3)
  11. nuget.org 无法加载源 https://api.nuget.org/v3/index.json 的服务索引
  12. ACM:《挑战程序设计竞赛》
  13. 50行Python代码制作一个计算器
  14. java通过十字路口_如何正确通过十字路口 老司机教你怎么走
  15. 微博插件-微博图片全显示(页面样式本人优化版)
  16. 工具提取MP4中的音视频
  17. ESP8266固件下载指南
  18. 仿穷游项目--bug集
  19. android app防被杀策略
  20. 上海世博呈科技盛宴 互动体验未来的衣食住行

热门文章

  1. Zcash - 各种密钥和签名,你懂吗?
  2. python内置函数有哪些_Python 中的内置函数(一)
  3. 去掉myeclipse项目中的js校验
  4. Style Transfer(PyTorch)
  5. python 仪表驱动_技术文章 | 锐视模块化仪器python驱动使用说明
  6. 基于 Vue JS、Element UI、Nuxt JS的项目PC端前端手册
  7. c语言程序中,整型常量的书写形式不包括_________.,??C语言程序中,整型常量的书写形式不包括_________。????...
  8. Qt如何读取.txt文件(将内容读到文本编辑框)
  9. python笔记(tornado初识)
  10. Python-提升爬虫速度三种方式