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

list部分接口模拟实现(c++)

List

  • list简介
  • list基本框架
    • list构造函数
      • list_node结构体的默认构造
      • list类的默认构造
    • push_back()
    • iteartor迭代器
    • 迭代器里面的其他接口
      • const迭代器
      • 通过模板参数实现复用
      • operator->()
    • insert()
    • erase()
    • clear()
    • 析构函数
    • 迭代器区间构造
    • 拷贝构造
    • operator=()

list简介

- list可以在常数范围内在任意位置进行插入和删除的序列式容器,并且容器可以双向迭代。
- list底层是一个带头双向链表结构,通过指针连接前一个和后一个元素。
- list支持在任意位置进行插入、删除元素,效率更好。
- list不支持随机访问

list基本框架

namespace xty
{//带头双向循环链表template<class T>struct list_node{list_node<T>* _prev;list_node<T>* _next;T _data;};template<class T>class list{typedef list_node<T> node;private:node* _head;//头指针};
}

list构造函数

我们实现一个无参构造函数,但是在这之前我们需要做一些准备工作,先实现一个申请list_node的构造函数,在struct类里面实现。

list_node结构体的默认构造

	//带头双向循环链表template<class T>struct list_node{list_node<T>* _prev;list_node<T>* _next;T _data;//在创建list_node变量时,自动调用构造list_node(const T& val = T()):_prev(nullptr),_next(nullptr),_data(val){}};

为什么不使用class,而使用struct呢?
因为我们想达到这样一个目的:想要让别人自由的访问自己的成员函数,不做任何限制,就用struct。而list_node,list是要经常使用的,因此使用struct修饰list_node比较方便。

list类的默认构造

仅仅申请一个空的头结点,next都指向头

在这里插入图片描述

		list(){//两种申请方法都可以//_head = new list_node<T>_head = new node;_head->next = _head;_head->prev = _head;//_head->_data已经在new的时候调用构造了}

push_back()

先记录一下尾结点,插入更简单。

		void push_back(const T& x){//留记录尾结点node* tail = _head->_prev;node* new_node = new node(x);//传入x值tail->_next = new_node;new_node->_next = _head;_head->_prev = new_node;new_node->_prev = tail;}

iteartor迭代器

整体框架:

	//iteartortemplate<class T>struct __list_iterator{typedef list_node<T> node;node* _node;//成员变量//构造函数__list_iterator(node* x):_node(x){}T& operator*(){return _node->_data;}//++返回相应的迭代器__list_iterator<T> operator++(){_node = _node->_next;return *this;}//是位置是否相等,不是内容是否相等。bool operator!=(__list_iterator<T> x){return _node != x._node;}};template<class T>class list{typedef list_node<T> node;public://迭代器重命名typedef __list_iterator<T> iterator;public://仅仅申请一个空的头结点,next都指向头list(){//两种申请方法都可以//_head = new list_node<T>_head = new node;_head->_next = _head;_head->_prev = _head;//_head->_data已经在new的时候调用构造了了}iterator begin(){iterator it(_head->_next);return it;}iterator end(){return iterator(_head);}

语法细节理解:

  1. 为什么把iterator和list进行单独封装?写一块不好吗?
    首先list只会用到迭代器的begin()和end()函数。其他的像++,其实都是通过迭代器的对象调用的(并不是list的对象调用的)。把iterator从list中抽离出来,不仅可以减少list的复杂性,还能让人更加清楚,迭代器其实也是一个模板,它能被抽离出来,用以实现各种数据结构的迭代!而list内部的begin和end接口,千篇一律。这样就达到的封装的目的!所以,还是分开写的好,逻辑更清晰,更容易维护。
  2. struct __list_iterator结构体里面为什么要写构造函数呢?
    在c++里struct也被当做是类!类里有构造函数很正常,还可以有拷贝构造呢!只不过struct默认是public的。这样我们在声明该类型的变量时,函数会自动调用构造函数,使该结构体的数据自动是被初始化过的。
	xty::list<int>::iterator it = lt.begin();  //调用对象需要用list//xty::list<int>::iterator it(lt.begin());  //调用对象需要用listwhile (it != lt.end()){cout << *it << endl;++it;}

写了构造函数之后,第二种声明方式也是可以的。而第一种方式其实调用的是拷贝构造函数,但是编译器给优化成了拷贝构造,我们没有写拷贝构造,编译器会调用默认的拷贝构造,是一个浅拷贝。但是我们不是经常说浅拷贝会造成析构问题?这里为什么不会?因为我们没有写析构函数,而且析构函数。为什么不写析构函数呢?因为没有什么可以析构的,函数的结点是list里的内容,不需要迭代器的析构函数管,因此我们不写析构函数。

  1. 迭代器++返回的是迭代器的类型。
  2. 注意:_list_iterator是类名,_list_iterator才是类型!
  3. list里面的begin要返回迭代器类型,然而怎么由指针转化成迭代器类型呢?要利用迭代器的构造函数来返回。

迭代器里面的其他接口

		bool operator==(const __list_iterator<T>& x){return _node == x._node;}//i++__list_iterator<T> operator++(int){__list_iterator<T> tem(*this);_node = _node->_next;return tem;}__list_iterator<T> operator--(){_node = _node->_prev;return *this;}__list_iterator<T> operator--(int){__list_iterator<T> tem(*this);_node = _node->_prev;return tem;}

语法细节理解:

  1. 注意迭代器传进来的参数基本上都是迭代器,如果不更改,最好加上const和&。
  2. 如果命名空间冲突,注意在函数声明和定义的时候也要加上空间名。
void Print(xty::list<int>& lt);
  1. 我们发现__list_iterator 有点长,我们重命名一下。
	//iteartortemplate<class T>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T> self;node* _node;//成员变量//构造函数__list_iterator(node* x):_node(x){}T& operator*(){return _node->_data;}//++返回相应的迭代器self operator++(){_node = _node->_next;return *this;}//是位置是否相等,不是内容是否相等。bool operator!=(const __list_iterator<T>& x){return _node != x._node;}bool operator==(const __list_iterator<T>& x){return _node == x._node;}//i++self operator++(int){self tem(*this);_node = _node->_next;return tem;}self operator--(){_node = _node->_prev;return *this;}self operator--(int){self tem(*this);_node = _node->_prev;return tem;}};

const迭代器

要实现const迭代器只需要再写一个const类即可。
记住是指针可以修改,但是内容不可以修改,因此不能在this那加const。

		//迭代器重命名typedef __list_iterator<T> iterator;typedef const__list_iterator<T> const_iterator;public:const_iterator begin()const{const_iterator it(_head->_next);return it;}const_iterator end()const{return const_iterator(_head);}

list里面的迭代器修改仅仅需要,typedef一下,然后将构造函数改成所需要的const类型即可。


而我们需要再实现一个const类型的iterator

	template<class T>struct const__list_iterator{typedef list_node<T> node;typedef const__list_iterator<T> self;node* _node;//成员变量//构造函数const__list_iterator(node* x):_node(x){}const T& operator*()  //值不仅可以修改!!{return _node->_data;}//++返回相应的迭代器self operator++()  //指针可以修改!!{_node = _node->_next;return *this;}//是位置是否相等,不是内容是否相等。bool operator!=(const self& x)const{return _node != x._node;}bool operator==(const self& x)const{return _node == x._node;}//i++self operator++(int)  //指针可以修改!!!{self tem(*this);_node = _node->_next;return tem;}self operator--()  //指针可以修改!!!{_node = _node->_prev;return *this;}self operator--(int)  //指针可以修改!!!{self tem(*this);_node = _node->_prev;return tem;}};

问题:代码写完之后,我们发现实际上只有operator*()的返回值加了const,其他的地方没有改,因此,我们利用模板参数赋用解决问题。

通过模板参数实现复用

	template<class T,class Ref>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T,Ref> self;node* _node;//成员变量//构造函数__list_iterator(node* x):_node(x){}Ref operator*(){return _node->_data;}...}template<class T>class list{typedef list_node<T> node;public://迭代器重命名typedef __list_iterator<T, T&> iterator;typedef __list_iterator<T,const T&> const_iterator;public://仅仅申请一个空的头结点,next都指向头list(){//两种申请方法都可以//_head = new list_node<T>_head = new node;_head->_next = _head;_head->_prev = _head;//_head->_data已经在new的时候调用构造了了}}

在这里插入图片描述

通过增加类模板参数,这种问题被很巧妙的解决了。请好好体会!

operator->()

当我们遇到自定义类型数据链表时,访问数据就会比较麻烦。

	struct AA{int _a1;int _a2;AA(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}};void test_aa(){xty::list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));lt.push_back(AA(4, 4));xty::list<AA>::iterator it = lt.begin();while (it != lt.end()){cout << (*it)._a1 << ":" << (*it)._a2 << endl;++it;}}

如上例子所示,cout方式,在这里很是别扭,因为it是迭代器,我们能不能通过重载->来访问这样的数据呢?
显然是可以的!如下:

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

所以我们通过如下方式打印链表的数据:

		xty::list<AA>::iterator it = lt.begin();while (it != lt.end()){//cout << (*it)._a1 << ":" << (*it)._a2 << endl;cout << it->_a1 << ":" << it->_a2 << endl;++it;}

但是这个代码是不是有一点别扭?没看出来的话,我再打印一次:

			//两种打印方式一样!!!cout << it->_a1 << ":" << it->_a2 << endl;cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << endl;

在这里插入图片描述
==解释:==之所以出现这样的原因,是因为编译器自动把两个连续的->优化成一个->,防止观感上的差异,这样设计能让人立马看出这个其实是在访问AA的数据。


为了适应const和非const两种迭代器,我们再设计一个模板参数。如下实例:

	//iteartortemplate<class T,class Ref, class Ptr>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T,Ref,Ptr> self;node* _node;//成员变量Ptr operator->(){return &(_node->_data);}
...................}
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;public:

insert()

		//在pos位置前插入,返回插入位置的迭代器iterator insert(iterator pos, const T& x){node* new_node = new node(x);node* cur = pos._node;node* prev = cur->_prev;prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;return iterator(cur);}

erase()

返回删除元素的下一个位置的迭代器。

		iterator erase(iterator pos){//不能删头assert(pos._node!=_head);node* cur = pos._node;node* prev = cur->_prev;prev->_next = cur->_next;cur->_next->_prev = prev;delete cur;return iterator(prev->_next)}

注意:删除元素后,pos位置的数据就被删除了,会产生迭代器失效问题,如果再使用pos需要重新赋值。

clear()

清空list的内容,可以借助迭代器和erase()来删除。

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

析构函数

结合clear()来编写。

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

迭代器区间构造

因为迭代器区间构造,也需要一个头结点,所以,我们方便复用,又定义了一个初始化函数,具体改动如下:

		list(){empty_init();//两种申请方法都可以//_head = new list_node<T>//_head = new node;//_head->_next = _head;//_head->_prev = _head;//_head->_data已经在new的时候调用构造了了}void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}template<class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);++first;}}

拷贝构造

提供了两种方法,第一种方法效率较低,第二种利用swap提高了效率。

		lt2(lt)//list(const list<T>& lt)//{//	empty_init();//	for (auto e : lt)//	{//		push_back(e);//	}//}void swap(list<T>& tem){std::swap(_head, tem._head);}list(const list<T>& lt){empty_init();//初始化thislist<T> tem(lt.begin(), lt.end());swap(tem);}

operator=()

实现较为简单。

//注意这里不能传引用list<T>& operator=(list<T> lt){swap(lt);return *this;}

最后一个问题:

const list<int> lt;

这行代码能调用构造函数吗?
答案是肯定的,因为变量在最开始是没有const属性的,定义好了以后,才有const属性。否则const都没法定义出来了。

相关文章:

list部分接口模拟实现(c++)

List list简介list基本框架list构造函数list_node结构体的默认构造list类的默认构造 push_back()iteartor迭代器迭代器里面的其他接口const迭代器通过模板参数实现复用operator->() insert()erase()clear()析构函数迭代器区间构造拷贝构造operator() list简介 - list可以在…...

数据结构(C语言) 实验-栈与字符串

删除子串 字符串采用带头结点的链表存储&#xff0c;设计算法函数void delstring(linkstring s, int i,int len) 在字符串s中删除从第i个位置开始&#xff0c;长度为len的子串。 void delstring(linkstring s, int i, int len) {linkstring p,q,r;int cnt 1;p s->next;wh…...

xLua Lua访问C#注意事项(七)

调用成员方法 注意:调用成员方法&#xff0c;第一个参数需要传该对象&#xff0c;建议用冒号语法 loacl camera CS.UnityEngine.GameObject.Find("Main Camera") --冒号语法 camera:GetComponent("Camera") --点语法 camera.GetComponent(camera,"…...

vue3+antv2.x的画布

报错信息&#xff1a; TypeError: Cannot destructure property component of registry_1.shapeMaps[node.shape] as it is undefined. at VueShapeView.renderVueComponent (http://192.168.10.35:9029/node_modules/.vite/deps/antv_x6-vue-shape.js?v49fbfab0:5569:19…...

Docker部署ubuntu1804镜像详细步骤

Docker部署ubuntu1804镜像详细步骤 ubuntu镜像库地址&#xff1a;https://hub.docker.com/_/ubuntu/tags?page1&ordering-name 拉取镜像&#xff08;默认为最新版本&#xff09;&#xff1a; docker pull ubuntu或&#xff0c;拉取指定版本镜像&#xff1a; docker pull…...

mac 卸载第三方输入法

输入法设置里的移除&#xff0c;并不是真的卸载&#xff0c;点击还是能添加回来 在活动监视器里强制退出此输入法在访达界面使用快捷键 ShiftcommandG在弹出的对话框内输入以下路径&#xff08;/资源库/Input Methods&#xff09;&#xff0c;再点击下面的前往找到你要卸载的输…...

可观察性在软件测试中的重要性

当今应用生态系统的需求和加速的数字化转型使可观察性成为人们关注的焦点。可观察性提供了对应用程序行为和技术生态系统的深入可见性&#xff0c;并支持更快、更明智的决策。由于缺乏可观察性&#xff0c;软件开发团队倾向于对生产系统行为、潜在性能瓶颈或未来故障场景做出假…...

Delphi TCP服务端监听端口获取客户端RFID网络读卡器上传的刷卡数据

本示例使用设备介绍&#xff1a;液显WIFI无线网络HTTP协议RFID云读卡器可编程实时可控开关TTS语-淘宝网 (taobao.com) unit Unit1;interfaceusesWindows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms,Dialogs, ComCtrls, ScktComp, StdCtrls, ScktCom…...

javaSE学习笔记(一)概述、语法

目录 前言 一、概述 1.java语言发展史 2.Java语言版本 3.Java语言平台 4.Java语言特点 5.Java语言跨平台原理-可移植性 6.JRE和JDK的概述 7.JDK的下载和安装 7.1安装的细节 7.2可能出现的问题 7.3验证安装是否成功 8.JDK安装路径下的目录解释 9.path环境变量的作…...

接口开发之使用C#插件Quartz.Net定时执行CMD任务工具

C#制作定时任务工具执行CMD命令 概要准备知识点实现原理thinkphp配置winform执行CMD命令读取ini配置文件定时任务Quartz.Net 完整代码Job.csIniFunc.csForm1.csconfig.ini简易定时任务工具雏形 概要 很多时候写接口上线后还会遇到很多修改&#xff0c;类似JAVA,C#,delphi制作的…...

XSS脚本(存储型xss获取肉鸡的cookies)

XSS脚本&#xff08;存储型xss获取肉鸡的cookies&#xff09; 存储型XSS就是在能够提交上传的文本框中提交一些标签代码&#xff0c;这段代码被插入到页面中&#xff0c;肉鸡每次点击这个页面时都会有弹框弹出。&#xff08;只要点击就会弹框&#xff09; 反射性XSS顾名思义插入…...

【React】04.MVC模式和MVVM模式

React是Web前端框架 1、目前市面上比较主流的前端框架 ReactAngular&#xff08;NG框架&#xff09;Vue 主流的思想&#xff1a; 不在直接去操作DOM&#xff0c;而是改为“数据驱动思想” 操作DOM思想&#xff1a; 操作DOM比较消耗性能[主要原因就是&#xff0c;可能会导…...

调试代码0

dev_update_off () * read_image (Image, C:/Users/Public/Documents/MVTec/HALCON-12.0/examples/images/smd/smd_on_chip_01.png) read_image (Image, D:/图像文件/图片/图片/基板/20230609-103004-0.bmp) get_image_size (Image, Width, Height) * dev_close_window () * de…...

【C++心愿便利店】No.12---C++之探索string底层实现

文章目录 前言一、写实拷贝&#xff08;了解&#xff09;二、string类常用接口实现2.1 成员变量2.2 默认构造函数2.3 拷贝构造函数2.4 operator2.5 operator[]2.6 c_str2.7 size()2.8 capacity() 三、迭代器的实现3.1 begin()和end()3.2 范围for 四、string类增删查改4.1 reser…...

Android Studio(列表视图ListView)

前言 前面在适配器章节&#xff0c;已经介绍了ListView的作用(干什么的)&#xff0c;这节将主要介绍如何去设计ListView页面视图。 思考 列表视图需要些什么&#xff1f; 1. 列表项容器&#xff08;装载各列表项的容器&#xff09;&#xff1a;<ListView/> 2. 列表项布局…...

让深度神经网络绘画以了解它们是如何工作的

一、说明 深度学习如此有效&#xff0c;这真是一个谜。尽管有一些关于深度神经网络为何如此有效的线索&#xff0c;但事实是没有人完全确定&#xff0c;并且深度学习的理论理解是一个非常活跃的研究领域。 在本教程中&#xff0c;我们将以一种不寻常的方式触及问题的一个小方面…...

https://www.jianshu.com/p/34bf240b85a9

https://www.jianshu.com/p/34bf240b85a9 https://www.eccee.com/soft-platform/991.html...

如何导出PPT画的图为高清图片?插入到world后不压缩图像的设置方法?

期刊投稿的时候&#xff0c;需要图片保持一定的清晰度数&#xff0c;那么我们怎么才能从PPT中导出符合要求的图片呢&#xff1f; 对于矢量图绘图软件所画的图&#xff0c;直接导出即可。 而PPT导出的图片清晰度在60pi&#xff0c;就很模糊。 整体思路&#xff1a; PPT绘图——…...

【Spring】Spring IOC DI

Spring IOC & DI IOC DI入门什么是Spring什么是容器什么是IOC IOC介绍传统程序开发解决方案 DI IOC详解Bean的存储Controller(控制器存储)Service(服务存储)Repository(仓库存储)Component(组件存储)Configuration(配置存储) 为什么需要这么多类注解类注解之间的关系方法注…...

一招解密网络流量瓶颈!

前言 我们曾介绍过观测云提供全面的基础设施监测方案&#xff08;参见《全方位监控基础设施&#xff0c;坚实守护您的业务稳定&#xff01;》&#xff09;&#xff0c;能够高效全面地帮助您实时观测所有的基础设施对象及云产品等&#xff0c;赋能您的业务稳定发展。今天我们将…...

深入剖析AI大模型:大模型时代的 Prompt 工程全解析

今天聊的内容&#xff0c;我认为是AI开发里面非常重要的内容。它在AI开发里无处不在&#xff0c;当你对 AI 助手说 "用李白的风格写一首关于人工智能的诗"&#xff0c;或者让翻译模型 "将这段合同翻译成商务日语" 时&#xff0c;输入的这句话就是 Prompt。…...

React第五十七节 Router中RouterProvider使用详解及注意事项

前言 在 React Router v6.4 中&#xff0c;RouterProvider 是一个核心组件&#xff0c;用于提供基于数据路由&#xff08;data routers&#xff09;的新型路由方案。 它替代了传统的 <BrowserRouter>&#xff0c;支持更强大的数据加载和操作功能&#xff08;如 loader 和…...

从深圳崛起的“机器之眼”:赴港乐动机器人的万亿赛道赶考路

进入2025年以来&#xff0c;尽管围绕人形机器人、具身智能等机器人赛道的质疑声不断&#xff0c;但全球市场热度依然高涨&#xff0c;入局者持续增加。 以国内市场为例&#xff0c;天眼查专业版数据显示&#xff0c;截至5月底&#xff0c;我国现存在业、存续状态的机器人相关企…...

MMaDA: Multimodal Large Diffusion Language Models

CODE &#xff1a; https://github.com/Gen-Verse/MMaDA Abstract 我们介绍了一种新型的多模态扩散基础模型MMaDA&#xff0c;它被设计用于在文本推理、多模态理解和文本到图像生成等不同领域实现卓越的性能。该方法的特点是三个关键创新:(i) MMaDA采用统一的扩散架构&#xf…...

Axios请求超时重发机制

Axios 超时重新请求实现方案 在 Axios 中实现超时重新请求可以通过以下几种方式&#xff1a; 1. 使用拦截器实现自动重试 import axios from axios;// 创建axios实例 const instance axios.create();// 设置超时时间 instance.defaults.timeout 5000;// 最大重试次数 cons…...

汇编常见指令

汇编常见指令 一、数据传送指令 指令功能示例说明MOV数据传送MOV EAX, 10将立即数 10 送入 EAXMOV [EBX], EAX将 EAX 值存入 EBX 指向的内存LEA加载有效地址LEA EAX, [EBX4]将 EBX4 的地址存入 EAX&#xff08;不访问内存&#xff09;XCHG交换数据XCHG EAX, EBX交换 EAX 和 EB…...

dify打造数据可视化图表

一、概述 在日常工作和学习中&#xff0c;我们经常需要和数据打交道。无论是分析报告、项目展示&#xff0c;还是简单的数据洞察&#xff0c;一个清晰直观的图表&#xff0c;往往能胜过千言万语。 一款能让数据可视化变得超级简单的 MCP Server&#xff0c;由蚂蚁集团 AntV 团队…...

基于matlab策略迭代和值迭代法的动态规划

经典的基于策略迭代和值迭代法的动态规划matlab代码&#xff0c;实现机器人的最优运输 Dynamic-Programming-master/Environment.pdf , 104724 Dynamic-Programming-master/README.md , 506 Dynamic-Programming-master/generalizedPolicyIteration.m , 1970 Dynamic-Programm…...

如何在网页里填写 PDF 表格?

有时候&#xff0c;你可能希望用户能在你的网站上填写 PDF 表单。然而&#xff0c;这件事并不简单&#xff0c;因为 PDF 并不是一种原生的网页格式。虽然浏览器可以显示 PDF 文件&#xff0c;但原生并不支持编辑或填写它们。更糟的是&#xff0c;如果你想收集表单数据&#xff…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...