C++语法——超详细模拟实现list源代码
这篇小编将以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源代码相关推荐
- Scala的基础语法(超详细版)
Scala的基础语法 文章目录 Scala的基础语法 1.声明值和变量 2.数据类型 3.算术和操作符重载 4.控制结构语句 4.1条件分支语句 4.2循环语句 5.方法与函数 5.1方法 5.2 函 ...
- spring boot整合freemarker及freemarker基础语法超详细讲解
采用模板+数据=HTML 实现页面的静态化. 也就是服务端的页面静态化技术. JSP/Freemarker/Thymeleaf是常见的模板引擎. 引依赖 okhttp与HttpClient一样的作用, ...
- 【数据结构】10分钟教你用栈求解迷宫老鼠问题超详细教程附C++源代码
问题描述 给定一张迷宫地图和一个迷宫入口,然后进入迷宫探索找到一个出口.如下图所示: 该图是一个矩形区域,有一个入口和出口.迷宫内部包含不能穿越的墙壁或者障碍物.这些障碍物沿着行和列放置,与迷宫的边界 ...
- python高级语法装饰器_Python高级编程——装饰器Decorator超详细讲解上
Python高级编程--装饰器Decorator超详细讲解(上篇) 送你小心心记得关注我哦!! 进入正文 全文摘要 装饰器decorator,是python语言的重要特性,我们平时都会遇到,无论是面向 ...
- Pyqt搭建YOLOV3目标检测界面(超详细+源代码)
Pyqt搭建YOLOV3目标检测界面(超详细+源代码) 2022.5.25更新 2021.11.23 更新 2021.11.22 更新 实现效果如下所示,可以检测图片.视频以及摄像头实时检测. 0.准 ...
- MySQL数据库快速入门到精通(超详细保姆级,建议收藏)这可能是目前最适合你的教程,从基础语法到实例演示。
前言 此文章旨在为需要掌握快速开发和复习MySQL的同学所准备,您完全可以把此文章当作参考文档来使用,本文将尽量精简,使您快速的理解和掌握语法. 关于MySQL MySQL是一个关系型数据库管理系统, ...
- JAVA使用HttpClient模拟登录正方教务系统,爬取学籍信息和课程表成绩等,超详细登录分析和代码注解
目录 前言 分析 代码实现 第一次GET POST登录 第二次Get 第三次GET 第四次GET 第五次GET 测试 完整代码 前言 最近在做一个APP,需要获取我们学校--武汉纺织大学皇家停水断电断 ...
- php - 超详细将 pdf 文档格式转换为图片格式,将 offce pdf 演示文稿转成图像 png / jpg(小白一看就懂的详细教程,附带完整示例源代码)
效果图 其他教程都有点乱而且有bug,本文站在新手小白的角度比较靠谱,超详细的完整流程及详细注释代码. 本文实现了 php 将 pdf 文档转为图片(png / jpg),完整流程及示例源代码, 你可 ...
- react的超详细讲解
create-react-app 项目目录 在HTML中使用react 1 2 3基础 React的注意事项 模拟的React 和 render React组件 函数组件 类组件 React 的数据源 ...
最新文章
- 二维绘图引擎:圆、半圆、弧线绘制
- 【重复制造精讲】4、计划初识
- SAP 移动类型详解
- office 自动编号系列碰到问题小解
- 十大经典数据挖掘算法:SVM
- IdentityServer4与ocelot实现认证与客户端统一入口
- 基于单链表的生产者消费者问题
- xgboost实例_XGBoost超详细推导,终于有人讲明白了!
- 云原生人物志|华为云CTO张宇昕:云原生已经进入深水区
- locust mysql_locust性能压测连接mysql,随机取出班级,绑定学生
- 学习Spring Boot:(十七)Spring Boot 中使用 Redis
- 《数字逻辑设计与计算机组成》一 第2章 2.1 简介
- 分数的计算机应用教案,计算机应用实训实验
- xcode6 使用MJRefresh
- BZOJ——T 1612: [Usaco2008 Jan]Cow Contest奶牛的比赛
- 趋势线与123法则应用图解,很多大佬偷偷在用
- 儿童节html5小游戏,六一儿童节游戏大全,六一儿童节游戏有哪些
- html如何设置顶部,css中文字如何设置在顶部
- 无人驾驶学习介绍和感悟
- BLE MESH组网(一)简介和基本概念