C++---list常用接口和模拟实现

list---模拟实现

  • list的简介
  • list函数的使用
    • 构造函数
    • 迭代器的使用
    • list的capacity
    • list element access
    • list modifiers
  • list的模拟实现
    • 构造函数,拷贝构造函数和=
    • 迭代器
    • begin和end
    • insert和erase
    • clear和析构函数
  • 源码

list的简介

list是用双向带头联表实现的一个容器,双向联表中每个元素存在互不相关的独立节点中,再节点中通过指针指向其前一个元素和后一个元素,并且可以再常数范围内再任意位置进行插入和删除的序列式容器。

list函数的使用

构造函数

构造函数( (constructor))接口说明
list (size_type n, const value_type& val = value_type())构造的list中包含n个值为val的元素
list()构造空的list
list (const list& x)拷贝构造函数
list (InputIterator first, InputIterator last)用[first, last)区间中的元素构造list
void ListTest2()
{std::list<int> a(4, 5);std::cout << "a的list" << ':';for (auto& e : a){std::cout << e << ' ';}std::cout << std::endl;std::list<int> b;//Emptystd::list<int> c(a.begin(), a.end());std::cout << "c的list" << ':';for (auto& e : c){std::cout << e << ' ';}}

在这里插入图片描述

迭代器的使用

函数声明接口说明
begin + end返回第一个元素的迭代器+返回最后一个元素下一个位置的迭代器
rbegin + rend返回第一个元素的reverse_iterator,即end位置,返回最后一个元素下一个位置的 reverse_iterator,即begin位置
	std::list<int> a(4, 5);std::cout << "a的list" << ':';std::list<int>::iterator it = a.begin();while (it != a.end()){std::cout << *it << ' ';++it;}

在这里插入图片描述

list的capacity

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

list element access

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

list modifiers

函数声明接口说明
push_front在list首元素前插入值为val的元素
pop_front删除list中第一个元素
push_back在list尾部插入值为val的元素
pop_back删除list中最后一个元素
insert在list position 位置中插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素

list的模拟实现

要想模拟实现list,首先我们可以定义一个节点类。为什么要定义一个节点类呢,想想数据结构中的双链表,每个元素都在独立的节点中,通过节点的前驱指针和后继指针指向前后位置。

list的模拟实现跟vector和string实现是不一样的,vector本质上是一个数组,数组本身就是一个迭代器,而list是一个个的节点,通过指针联系在一起,所以vector类不用对迭代器进行封装,可以直接使用,list不一样,节点++是什么是不清楚的,这个时候可以使用运算符重载,对++,–,*等进行重载。

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

再定义一个list类,再这个类中,完成其他函数的实现

	template<class T>class list{public:private:Node* _head;size_t _size;};

构造函数,拷贝构造函数和=

list的构造函数实现,跟双链表中的初始化是一样的,将next指针和prev指针都指向头节点,形成一个环。

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

拷贝构造函数再拷贝之前,要先开一个头节点的空间,然后再头节点后面依次把值进行尾插即可。

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

赋值重载=和vector里面的写法是一样的。

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

通过传参创建出一个临时变量,然后将其交换。

迭代器

要想实现list的迭代器,需要将原生态指针进行封装。

list是由一个个的节点组成,节点++,解引用,–,等操作是找不到对应的数据的,所以需要用自定义类型对指针进行封装,从而完成一系列操作。

迭代器有一个const类型的迭代器,有一个无const类型的,再实现一个无const类型的迭代器之后,把代码赋值粘贴,也可以改成带const类型的迭代器,但这样显然是代码冗余的,重复的太多,这个时候,那些大佬们就通过增加模板参数,来解决代码冗余,根据参数的类型,实列化出不同的类。

	
//typedef __list_iterator<T, T&, T*> iterator;
//typedef __list_iterator<T,const T&,const T*> const_iterator;
template<class T,class Ref,class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;//构造方法__list_iterator(Node* node):_node(node){}//对节点解引用,是找不到我们存储的数据的,因为节点里面存的是val,next和prev,重载的目是使其对迭代器解引用可以找到有效数据Ref operator*(){return _node->_val;}Ptr operator->(){return &(_node->val);}//指针++,到下一个位置,其实就是让节点向后移动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 *this;}bool operator !=(const self& it) const{return _node != it._node;}bool operator==(const self& it) const{return _node == it._node;}};

以上就是用自定义的类,对指针进行封装。

begin和end

对指针进行封装之后,就可以实现begin和end了。

begin只要返回头节点的下一个节点即可

end就是头节点

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

insert和erase

有一个insert,就可以完成再任意位置插入。

		void push_back(const T& x){insert(end(), x);}void push_front(const T& x){insert(begin(), x);}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;newnode->_prev = prev;cur->_prev = newnode;return newnode;}

先找到当前节点cur和当前节点的上一个节点prev,再new出一个要插入的节点newnode,使得cur和newnode进行双向连接,prev和newnode进行双向连接。


删除一个节点,只需要让当前节点的上一个节点和下一个节点完成双向连接即可。

		iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;}

clear和析构函数

		void clear(){iterator it = begin();while (it != end()){it = earse(it);}_size = 0;}~list(){}

源码

#pragma once#include <assert.h>
#include <algorithm>namespace HaiFan
{template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _val;list_node(const T& val = T()):_next(nullptr),_prev(nullptr),_val(val){}};template<class T,class Ref,class Ptr>struct __list_iterator{typedef list_node<T> Node;typedef __list_iterator<T, Ref, Ptr> self;Node* _node;__list_iterator(Node* node):_node(node){}T& operator*(){return _node->_val;}T* operator->(){return &(_node->val);}T& operator++(){_node = _node->_next;return *this;}T operator++(int){self tmp(*this);_node = _node->_next;return tmp;}T& operator--(){_node = _node->_prev;return *this;}T operator--(int){self tmp(*this);_node = _node->prev;return *this;}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 list_node<T> Node;public:typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<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(){EmptyInit();}list(const list<T>& it){EmptyInit();for (auto& e : it){push_back(e);}}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);}void clear(){iterator it = begin();while (it != end()){it = earse(it);}_size = 0;}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());}iterator insert(iterator pos, const T& x){Node* cur = pos._node;Node* prev = cur->_prev;Node* newnode = new Node(x);prev->_next = newnode;newnode->_next = cur;newnode->_prev = prev;cur->_prev = newnode;return newnode;}iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;prev->_next = next;next->_prev = prev;delete cur;--_size;return next;}size_t size(){return _size;}void EmptyInit(){_head = new Node();_head->_next = _head;_head->_prev = _head;_size = 0;}~list(){}private:Node* _head;size_t _size;};
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/24977.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

前端笔记html-layer使用

layer.open方法 layer.open({type:2, //可传入的值有&#xff1a;0&#xff08;信息框&#xff0c;默认&#xff09;1&#xff08;页面层&#xff09;2&#xff08;iframe层&#xff09;3&#xff08;加载层&#xff09;4&#xff08;tips层&#xff09;title: title,content:[…

Python-flask项目入门

一、flask对于简单搭建一个基于python语言-的web项目非常简单 二、项目目录 示例代码 git路径 三、代码介绍 1、安装pip依赖 通过pip插入数据驱动依赖pip install flask-sqlalchemy 和 pip install pymysql 2.配置数据源 config.py DIALECT mysql DRIVER pymysql USERN…

Spring(11) Bean的生命周期

目录 一、简介二、Bean的流程1.BeanDefinition2.Bean 的生命周期 三、代码验证1.User 实体类2.MyBeanPostProcessor 后置处理器3.SpringConfig 扫描包配置4.UserTest 测试类5.测试结果6.模拟AOP增强 一、简介 首先&#xff0c;为什么要学习 Spring 中 Bean 的生命周期呢&#…

反弹shell的N种姿势

预备知识1. 关于反弹shell 就是控制端监听在某TCP/UDP端口&#xff0c;被控端发起请求到该端口&#xff0c;并将其命令行的输入输出转到控制端。reverse shell与telnet&#xff0c;ssh等标准shell对应&#xff0c;本质上是网络概念的客户端与服务端的角色反转。2. 反弹shel…

webpack性能优化

文章目录 1. 性能优化-分包2. 动态导入3. 自定义分包4. Prefetch和Preload5. CDN加载配置6. CSS的提取7. terser压缩7.1 Terser在webpack中配置7.2 css压缩 8. Tree Shaking 消除未使用的代码8.1 usedExports 配置8.2 sideEffects配置8.3 CSS实现Tree Shaking 9. Scope Hoistin…

并查集练习 — 扩展问题(二)

根据并查集练习 —岛屿数量的问题再次扩展&#xff1a; 原题是给定一个二维数组matrix&#xff08;char[][]&#xff09;&#xff0c;里面的值不是1就是0&#xff0c;上、下、左、右相邻的1认为是一片岛。返回matrix中岛的数量。 扩展为&#xff1a;如果是中国的地图&#xff0…

Xposed回发android.os.NetworkOnMainThreadException修复

最近用xposed进行hook回发的时候&#xff0c;又出现了新的问题&#xff1b; android.os.NetworkOnMainThreadException&#xff1b; 在Android4.0以后&#xff0c;写在主线程&#xff08;就是Activity&#xff09;中的HTTP请求&#xff0c;运行时都会报错&#xff0c;这是因为…

移动开发最佳实践:为 Android 和 iOS 构建成功应用的策略

您可以将本文作为指南&#xff0c;确保您的应用程序符合可行的最重要标准。请注意&#xff0c;这份清单远非详尽无遗&#xff1b;您可以加以利用&#xff0c;并添加一些自己的见解。 了解您的目标受众 要制作一个成功的应用程序&#xff0c;你需要了解你是为谁制作的。从创建…

API接口:企业信息核验

企业信息核验是现代企业管理中必不可少的一项业务&#xff0c;它可以帮助企业做出正确的决策。在这篇文章里&#xff0c;我们将会介绍如何使用API接口来对企业信息进行核验&#xff0c;并实现快捷、准确的查询。 一、API接口 在这里我们使用的是挖数据提供的企业信息核验API接…

arm交叉编译lmbench

一、下载lmbench www.bitmover.com/lmbench 官网下载&#xff0c;http://www.bitmover.com/lmbench/lmbench3.tar.gz 我没有下载下来&#xff0c;找的别人的百度云下载 链接: https://pan.baidu.com/s/1tGo1clCqY-jQPN8G1eWSsg 提取码: f6jd 二、修改makefile 修改三个文件…

Last-Mile Embodied Visual Navigation 论文阅读

论文阅读 题目&#xff1a;Last-Mile Embodied Visual Navigation 作者&#xff1a;JustinWasserman, Karmesh Yadav 来源&#xff1a;CoRL 时间&#xff1a;2023 代码地址&#xff1a;https://jbwasse2.github.io/portfolio/SLING Abstract 现实的长期任务&#xff08;例如…

W5500-EVB-PICO做DNS Client进行域名解析(四)

前言 在上一章节中我们用W5500-EVB-PICO通过dhcp获取ip地址&#xff08;网关&#xff0c;子网掩码&#xff0c;dns服务器&#xff09;等信息&#xff0c;给我们的开发板配置网络信息&#xff0c;成功的接入网络中&#xff0c;那么本章将教大家如何让我们的开发板进行DNS域名解析…

小程序 view下拉滑动导致scrollview滑动事件失效

小程序页面需要滑动功能 下拉时滑动&#xff0c;展示整个会员卡内容&#xff0c; 下拉view里包含了最近播放&#xff1a;有scrollview&#xff0c;加了下拉功能后&#xff0c;scrollview滑动失败了。 <view class"cover-section" catchtouchstart"handletou…

CS 144 Lab Four 收尾 -- 网络交互全流程解析

CS 144 Lab Four 收尾 -- 网络交互全流程解析 引言Tun/Tap简介tcp_ipv4.cc文件配置信息初始化cs144实现的fd家族体系基于自定义fd体系进行数据读写的adapter适配器体系自定义socket体系自定义事件循环EventLoop模板类TCPSpongeSocket详解listen_and_accept方法_tcp_main方法_in…

[整合]无root权限的服务器上安装screen

安装ncurses 命令行输入以下指令安装ncurses&#xff0c;这个是安装screen的前置依赖包。 wget http://ftp.gnu.org/gnu/ncurses/ncurses-6.0.tar.gz #下载所需的ncurses版本tar zxvf ncurses-6.0.tar.gz #解压到当前目录cd ncurses-6.0 #打开ncurses-6.0文件夹mkdi…

---------------- 部署 Zookeeper 集群 ----------------

部署 Zookeeper 集群 1.安装前准备2.安装 Zookeeper修改配置文件在每个节点上创建数据目录和日志目录在每个节点的dataDir指定的目录下创建一个 myid 的文件配置 Zookeeper 启动脚本 //准备 3 台服务器做 Zookeeper 集群 192.168.109.1 192.168.109.2 192.168.109.3 1.安装前准…

实现vscode上用gdb调试stm32

实现vscode上用gdb调试stm32 这周负责编写设备的某个模块&#xff0c;其中遇到了一些变量地址不正确的错误&#xff0c;按理这种底层变量错误用gdb一类的调试器就能很快查到&#xff0c;可是初入嵌入式一行&#xff0c;此C语言非彼C语言&#xff0c;对于gdb怎么对接到项目上根…

ffmpeg-ffplay代码架构简述

全局变量 /* Minimum SDL audio buffer size, in samples. */ // 最小音频缓冲 #define SDL_AUDIO_MIN_BUFFER_SIZE 512 /* Calculate actual buffer size keeping in mind not cause too frequent audio callbacks */ // 计算实际音频缓冲大小&#xff0c;并不需要太频繁…

浅谈React中的ref和useRef

目录 什么是useRef&#xff1f; 使用 ref 访问 DOM 元素 Ref和useRef之间的区别 Ref和useRef的使用案例 善用工具 结论 在各种 JavaScript 库和框架中&#xff0c;React 因其开发人员友好性和支持性而得到认可。 大多数开发人员发现 React 非常舒适且可扩展&#xff0c;…

51单片机(普中HC6800-EM3 V3.0)实验例程软件分析 实验三 LED流水灯

目录 前言 一、原理图及知识点介绍 二、代码分析 知识点五&#xff1a;#include 中的库函数解析 _crol_&#xff0c;_irol_&#xff0c;_lrol_ _cror_&#xff0c;_iror_&#xff0c;_lror_ _nop_ _testbit_ 前言 第一个实验:51单片机&#xff08;普中HC6800-EM3 V3.0…