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

C++第二十五弹---从零开始模拟STL中的list(下)

 ✨个人主页: 熬夜学编程的小林

💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】

目录

1、函数补充

2、迭代器完善

3、const迭代器

总结


1、函数补充

拷贝构造

思路:

  • 先构造一个头结点,然后将 lt 类中的元素依次尾插到新的结点上。
void empty_init()
{_head = new Node;_head->_next = _head;_head->_prev = _head;_size = 0;
}
list(const list<T>& lt)
{empty_init();//构造一个头结点for (auto& x : lt){push_back(x);}
}

 {}初始化构造

思路:

  • 先构造一个头结点,然后将 il 类中的元素依次尾插到新的结点上。
list(initializer_list<T> il)
{empty_init();for (auto& x : il){push_back(x);}
}

赋值操作符重载

void swap(list<T>& lt)
{std::swap(_head, lt._head);std::swap(_size, lt._size);
}
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

大小相关函数

size_t size()
{return _size;
}
bool empty()
{return _size == 0;
}

clear()

清空list的内容,保留头结点。

//清空数据
void clear()
{iterator it = begin();while (it != end()){it = erase(it);//更新迭代器}
}

~list()

析构函数,清空list的内容并释放头结点。

~list()
{clear();//清空内容函数delete _head;//释放头结点_head = nullptr;//置空
}

2、迭代器完善

前面我们处理的都是内置类型的情况,如果我们出现自定义类型,如何解决?

自定义类型举例:

struct A
{int _a1;int _a2;A(int a1 = 0, int a2 = 0):_a1(a1), _a2(a2){}
};

 首先我们先看看几种自定义类型的尾插方式:

void test_list3()
{list<A> lt;A aa1(1, 1);//实例化对象A aa2{ 2,2 };//多参数的隐式类型转换,C++11lt.push_back(aa1);//有名对象实例化lt.push_back(aa2);lt.push_back(A(3, 3));//匿名对象lt.push_back({ 4,4 });//多参数的隐式类型转换,C++11
}

 对自定义类型进行遍历:

list<A>::iterator it = lt.begin();
while (it != lt.end())
{cout << *it << " ";//自定义类型输出不了it++;
}
cout << endl;

A是自定义类型,不支持留插入,我们解引用得到的_data是A的对象 。在结构体中我们获取到自定义类型的对象可以通过 . 进行访问内部成员,此处我们也可以使用 . 进行访问内部成员。

cout << (*it)._a1 << ":" << (*it)._a2 << " ";

但是如果这么使用会有一点别捏,我们在自定义类型中,也可以通过自定义类型的地址来访问成员,即通过 ->访问,此处我们也可以通过 ->进行访问,因此我们需要重载一个operator->()函数 。

迭代器类中重载operator->

T* operator->()
{return &_node->_data;//取数据的地址
}

使用->访问元素

cout << it->_a1 << ":" << it->_a2 << " ";

使用重载函数版

cout << it.operator->()->_a1 << ":" << it.operator->()->_a2 << " ";

测试结果:

注意:

这里隐藏了一个箭头一个是重载一个是原生指针的访问操作。

当重载 operator->,不会直接返回成员的值,而是应该返回一个指针,这个指针指向的对象包含我们想要访问的成员。当使用 ->运算符时,C++ 会自动和透明地调用重载的 operator-> 并继续 “链式” 访问成员,而不需要程序员显示地添加多余的箭头。 

3、const迭代器

 我们上一弹写的普通迭代器对于const对象是无法编译成功的,const不能调用非const成员函数(权限放大)。

下面我们则实现一个const迭代器的类。

与普通迭代器类似,我们需要先在list类中重命名一个const迭代器

typedef ListConstIterator<T> const_iterator;//const迭代器类const_iterator begin() const
{return const_iterator(_head->_next);//匿名对象//return _head->_next;//单参数类型转换
}
const_iterator end() const
{return const_iterator(_head);
}

注意:

const迭代器名字不能写成 const iterator,因为const迭代器的本质是迭代器指向的内容不能修改,而不是迭代器本身不能修改,const_iterator这样定义是迭代器不能修改,内容还是可以修改的

实现const_iterator类有两种方式,如下:

方式一(单独实现一个新的类,修改普通迭代器的部分地方):

template<class T>
struct ListConstIterator
{typedef ListConstIterator<T> Self;//对迭代器类重定义typedef ListNode<T> Node;Node* _node;//构造ListConstIterator(Node* node):_node(node){}const T& operator*()//只能访问,不能修改值{return _node->_data;}const T* operator->(){return &_node->_data;//返回指针}//前置++ Self& operator++(){_node = _node->_next;return *this;}//后置++Self operator++(int){Self tmp(*this);_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};

我们可以看到const迭代器与普通迭代器区间只在operator*与operator->的返回的类型上,那么我们是不是可以将两个类封装成一个模板类呢???

//普通迭代器和const迭代器只有两个返回值不同,因此我们使用模板封装
template<class T, class Ref, class Ptr>//reference引用 point指针
struct ListIterator
{typedef ListIterator<T, Ref, Ptr> Self;//对迭代器类重定义typedef ListNode<T> Node;Node* _node;//构造ListIterator(Node* node):_node(node){}//T& operator*()//遍历及修改Ref operator*(){return _node->_data;}//T* operator->()Ptr operator->(){return &_node->_data;//返回指针}//前置++ 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 tmp;//返回临时变量}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}
};

合并之后的三个类模板参数:

  • T链表结点存储_data值的数据类型
  • Ref:通过迭代器访问数据时的返回类型,可以是T&或者const T&。
  • Ptr:通过迭代器访问数据的指针类型,可以是T*或者const T*

链表实例化如下:

typedef ListIterator<T, T&, T*> iterator;//普通迭代器类typedef ListIterator<T, const T&, const T*> const_iterator;//const迭代器类

 list实现全部代码

namespace lin
{//链表基本结构template<class T>struct ListNode{ListNode<T>* _prev;ListNode<T>* _next;T _data;ListNode(const T& val = T())//初始化值构造:_prev(nullptr),_next(nullptr),_data(val){}};//原版普通迭代器//迭代器操作类 方法都要被访问,使用struct//template<class T>//struct ListIterator//{//	typedef ListIterator<T> Self;//对迭代器类重定义//	typedef ListNode<T> Node;//	Node* _node;//	//构造//	ListIterator(Node* node)//		:_node(node)//	{}//	T& operator*()//遍历及修改//	{//		return _node->_data;//	}//	T* operator->()//	{//		return &_node->_data;//返回指针//	}//	//前置++ //	Self& operator++()//	{//		_node = _node->_next;//		return *this;//	}//	//后置++//	Self operator++(int)//	{//		Self tmp(*this);//		_node = _node->_next;//		return *this;//	}//	bool operator!=(const Self& it)//	{//		return _node != it._node;//	}//	bool operator==(const Self& it)//	{//		return _node == it._node;//	}//};//原版const迭代器//template<class T>//struct ListConstIterator//{//	typedef ListConstIterator<T> Self;//对迭代器类重定义//	typedef ListNode<T> Node;//	Node* _node;//	//构造//	ListConstIterator(Node* node)//		:_node(node)//	{}//	const T& operator*()//只能访问,不能修改值//	{//		return _node->_data;//	}//	const T* operator->()//	{//		return &_node->_data;//返回指针//	}//	//前置++ //	Self& operator++()//	{//		_node = _node->_next;//		return *this;//	}//	//后置++//	Self operator++(int)//	{//		Self tmp(*this);//		_node = _node->_next;//		return *this;//	}//	Self& operator--()//	{//		_node = _node->_prev;//		return *this;//	}//	Self operator--(int)//	{//		Self tmp(*this);//		_node = _node->_prev;//		return tmp;//	}//	bool operator!=(const Self& it)//	{//		return _node != it._node;//	}//	bool operator==(const Self& it)//	{//		return _node == it._node;//	}//};//普通迭代器和const迭代器只有两个返回值不同,因此我们使用模板封装template<class T, class Ref, class Ptr>//reference引用 point指针struct ListIterator{typedef ListIterator<T,Ref,Ptr> Self;//对迭代器类重定义typedef ListNode<T> Node;Node* _node;//构造ListIterator(Node* node):_node(node){}//T& operator*()//遍历及修改Ref operator*(){return _node->_data;}//T* operator->()Ptr operator->(){return &_node->_data;//返回指针}//前置++ 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 tmp;//返回临时变量}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};template<class T>class list{typedef ListNode<T> Node;//将链表结构重命名public://普通版本//typedef ListIterator<T> iterator;//需要被访问,放在public内//typedef ListConstIterator<T> const_iterator;//const迭代器类//类模板typedef ListIterator<T,T&,T*> iterator;//需要被访问,放在public内typedef ListIterator<T,const T&,const T*> const_iterator;//const迭代器类//构造哨兵结点void empty_init(){_head = new Node;_head->_next = _head;_head->_prev = _head;}list()//默认构造{empty_init();//创建哨兵头结点}size_t size(){return _size;}void clear()//清空数据,不销毁哨兵头结点{iterator it = begin();while (it != end()){it = erase(it);}}~list()//析构函数{clear();delete _head;_head = nullptr;}list(const list<T>& lt)//拷贝构造{empty_init();//创建头结点,然后进行尾插for (auto& x : lt){push_back(x);}}void swap(list<T>& lt){std::swap(_head, lt._head);std::swap(_size, lt._size);}list<T>& operator=(list<T> lt){swap(lt);return *this;}iterator begin() {return iterator(_head->_next);//匿名对象//return _head->_next;//单参数类型转换}iterator end() {return iterator(_head);}//解决打印修改值问题const_iterator begin() const{return const_iterator(_head->_next);//匿名对象//return _head->_next;//单参数类型转换}const_iterator end() const{return const_iterator(_head);}//单独实现的尾插//void push_back(const T& val)//{//	//tail //	Node* newnode = new Node(val);//	Node* tail = _head->_prev;//	tail->_next = newnode;//	newnode->_prev = tail;//	newnode->_next = _head;//	_head->_prev = newnode;//}void insert(iterator pos, const T& val)//在pos位置前插入val{Node* cur = pos._node;Node* newnode = new Node(val);Node* prev = cur->_prev;//prev newnode curnewnode->_next = cur;cur->_prev = newnode;prev->_next = newnode;newnode->_prev = prev;_size++;}iterator erase(iterator pos)//删除pos位置,防止迭代器失效,返回迭代器后一个位置{Node* cur = pos._node;Node* prev = cur->_prev;Node* next = cur->_next;//prev nextprev->_next = next;next->_prev = prev;delete cur;_size--;return iterator(next);}//调用insert函数void push_back(const T& val){//insert(--begin(),val);//不能使用+n,在--begin前面插入insert(end(), val);//end()前面}void push_front(const T& val){insert(begin(), val);//begin()前面插入}void pop_back(){erase(--end());//end()前面删除}void pop_front(){erase(begin());//begin()位置删除}private:Node* _head;//链表成员变量size_t _size;//链表大小};
}

总结


本篇博客就结束啦,谢谢大家的观看,如果公主少年们有好的建议可以留言喔,谢谢大家啦!

相关文章:

C++第二十五弹---从零开始模拟STL中的list(下)

✨个人主页&#xff1a; 熬夜学编程的小林 &#x1f497;系列专栏&#xff1a; 【C语言详解】 【数据结构详解】【C详解】 目录 1、函数补充 2、迭代器完善 3、const迭代器 总结 1、函数补充 拷贝构造 思路&#xff1a; 先构造一个头结点&#xff0c;然后将 lt 类中的元…...

STM32/keil把多个c文件编译为静态库lib

把常用的、不经常修改的代码库编译成lib以后&#xff0c;可以加快整个工程的编译速度。 一个常见的应用场景就是&#xff0c;把ST的标准库或HAL库等编译成lib&#xff0c;这样以后再编译整个工程时&#xff0c;就无需再次编译他们了&#xff0c;可以节省编译时间。当然&#x…...

L45---506.相对名次(java)--排序

1.题目描述 2.知识点 &#xff08;1&#xff09;String.join(" ", words) 是 Java 中的一个语法&#xff0c;用于将数组或集合中的元素连接成一个单独的字符串&#xff0c;连接时使用指定的分隔符。这里的 " " 是作为分隔符使用的一个空格字符串。 Strin…...

跨网段路由

跨网段路由通常是指在网络中配置路由&#xff0c;以允许不同子网之间的通信。要设置跨网段的永久路由&#xff0c;取决于你是在操作路由器、交换机这样的网络设备&#xff0c;还是在配置个人计算机&#xff08;如Windows或Linux系统&#xff09;。下面是两种常见情况下的简要指…...

HO-3D 数据集

// 由于非刚体的追踪比较困难&#xff0c;所以看看刚体数据集 HOnnotate: A method for 3D Annotation of Hand and Object Poses // cvpr20https://arxiv.org/abs/1907.01481 https://github.com/shreyashampali/ho3d https://paperswithcode.com/paper/ho-3d-a-mult…...

Elasticsearch 认证模拟题 - 8

一、题目 在集群中输入以下指令&#xff1a; PUT phones/_doc/1 {"brand":"Samsumg","model":"Galaxy S9","features":[{"type":"os", "value":"Android"},{"type":&q…...

【Postman接口测试】第四节.Postman接口测试项目实战(中)

文章目录 前言五、Postman断言 5.1 Postman断言介绍 5.2 响应状态码断言 5.3 包含指定字符串断言 5.4 JSON数据断言六、参数化 5.1 Postman参数化介绍 5.2 Postman参数化实现 5.3 针对项目登录接口参数化实现 总结 前言 五、Postman断言 5.1 Postman断言介…...

Hadoop的Windows环境准备

一、将Hadoop传输到Windows中 1、备份副本 cp -r /opt/softs/hadoop3.1.3/ /opt/softs/hadoop3.1.3_temp 2、删除备份的share目录 cd /opt/softs/hadoop3.1.3_temp rm -rf share/ 3、下载到Windows中 重命名去掉_temp 4、删除备份文件 rm -rf /opt/softs/hadoop3.1.3_t…...

使用亮数据代理IP爬取PubMed文章链接和邮箱地址

&#x1f482; 个人网站:【 摸鱼游戏】【神级代码资源网站】【工具大全】&#x1f91f; 一站式轻松构建小程序、Web网站、移动应用&#xff1a;&#x1f449;注册地址&#x1f91f; 基于Web端打造的&#xff1a;&#x1f449;轻量化工具创作平台&#x1f485; 想寻找共同学习交…...

electron调用dll时应用程序闪退

electron调用dll时&#xff0c;直接闪退&#xff0c;且用如下方式监听无任何输出&#xff1a; window-all-closed 或 will-quit 此时需要检查传给dll的参数及参数类型是否正确&#xff0c;特别是使用ffi-napi时调用dll&#xff0c;使用 ref-napi定义类型&#xff0c;经常容易…...

单片机原理及技术(三)—— AT89S51单片机(二)(C51编程)

一、AT89S51单片机的并行I/O端口 1.1 P0口 AT89S51的P0口是一个通用的I/O口&#xff0c;可以用于输入和输出。每个引脚都可以通过软件控制为输入或输出模式。 1.1.1 P0口的工作原理 P0口的工作原理是通过对P0寄存器的读写操作来控制P0口的引脚。 输出模式&#xff1a;当P0口…...

摄影店展示服务预约小程序的作用是什么

摄影店包含婚照、毕业照、写真、儿童照、工作照等多个服务项目&#xff0c;虽然如今人们手机打开便可随时拍照摄影&#xff0c;但在专业程度和场景应用方面&#xff0c;却是需要前往专业门店服务获取。 除了进店&#xff0c;也有外部预约及活动、同行合作等场景&#xff0c;重…...

【数据分析基础】实验一 Python运算符、内置函数、序列基本用法

一、实验目的 熟练运用Python运算符。熟练运用Python内置函数。掌握Python的基本输入输出方法。了解lambda表达式作为函数参数的用法。掌握列表、元组、字典、集合的概念和基本用法。了解Python函数式编程模式。 二、实验内容&#xff1a; 1. 在命令模式测试如下命令&#x…...

【Redis】构建强韧的远程Redis连接与端口保障机制完美指南

【Redis】构建强韧的远程Redis连接与端口保障机制完美指南 大家好 我是寸铁&#x1f44a; 总结了【Redis】构建强韧的远程Redis连接与端口保障机制完美指南✨ 喜欢的小伙伴可以点点关注 &#x1f49d; 前言 在当今的软件开发领域中&#xff0c;远程访问和操作数据存储是极为常见…...

Flowable项目启动报错#java.time.LocalDateTime cannot be cast to java.lang.String

Flowable 项目启动后报错 flow项目第一次启动创建表成功&#xff0c;但是第二次启动时报错信息如下&#xff1a; 1、Error creating bean with name ‘appRepositoryServiceBean’ defined in class 2、Error creating bean with name ‘flowableAppEngine’: FactoryBean t…...

《数字电路》

问答题4*5 在数字电路中&#xff0c;三极管经常工作在哪两种开关状态&#xff1f; 在数字电路中&#xff0c;三极管经常工作在饱和导通状态和截止状态。 时序电路根据输出信号分为哪两类&#xff1f; 时序电路根据输出信号分为莫尔型和米里型两类。 写出三种以上常用的二-十…...

STM32F103 点亮LED闪烁与仿真

STM32F103 点亮LED闪烁与仿真 今天给大家分享一下STM32 流水灯简单的仿真吧&#xff0c;我感觉这个提供有用的&#xff0c;但是自己也是第一次使用&#xff0c;主要是感觉曲线很高级。在PWM中查看脉宽很有用。 code: led.c #include "led.h" #include "delay…...

阿里云服务器发送邮件失败 Could not connect to SMTP host: smtp.xxx.com, port: 465;

最近做了一个发送邮件的功能&#xff0c; 在本地调试完成后&#xff0c;部署到阿里云服务器就一直报错&#xff0c; Could not connect to SMTP host: smtp.qiye.aliyun.com, port: 465; 网上也搜索了很多的资料&#xff0c;最后花了好几个小时才解决&#xff0c; 报错日志如下…...

Socket编程权威指南(二)完美掌握TCP流式协议及Socket编程的recv()和send()

在上一篇文章中&#xff0c;我们学习了Socket编程的基础知识&#xff0c;包括创建Socket、绑定地址、监听连接、接收连接等操作。然而&#xff0c;真正的套接字编程远不止于此。本文将重点介绍TCP 流式协议&#xff0c;什么是粘包问题&#xff1f;如何解决粘包问题 &#xff1f…...

当C++的static遇上了继承

比如我们想要统计下当前类被实例化了多少次&#xff0c;我们通常会这么写 class A { public:A() { Count_; }~A() { Count_--; }int GetCount() { return Count_; }private:static int Count_; };class B { public:B() { Count_; }~B() { Count_--; }int GetCount() { return …...

8k长序列建模,蛋白质语言模型Prot42仅利用目标蛋白序列即可生成高亲和力结合剂

蛋白质结合剂&#xff08;如抗体、抑制肽&#xff09;在疾病诊断、成像分析及靶向药物递送等关键场景中发挥着不可替代的作用。传统上&#xff0c;高特异性蛋白质结合剂的开发高度依赖噬菌体展示、定向进化等实验技术&#xff0c;但这类方法普遍面临资源消耗巨大、研发周期冗长…...

cf2117E

原题链接&#xff1a;https://codeforces.com/contest/2117/problem/E 题目背景&#xff1a; 给定两个数组a,b&#xff0c;可以执行多次以下操作&#xff1a;选择 i (1 < i < n - 1)&#xff0c;并设置 或&#xff0c;也可以在执行上述操作前执行一次删除任意 和 。求…...

C++ 基础特性深度解析

目录 引言 一、命名空间&#xff08;namespace&#xff09; C 中的命名空间​ 与 C 语言的对比​ 二、缺省参数​ C 中的缺省参数​ 与 C 语言的对比​ 三、引用&#xff08;reference&#xff09;​ C 中的引用​ 与 C 语言的对比​ 四、inline&#xff08;内联函数…...

LLM基础1_语言模型如何处理文本

基于GitHub项目&#xff1a;https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken&#xff1a;OpenAI开发的专业"分词器" torch&#xff1a;Facebook开发的强力计算引擎&#xff0c;相当于超级计算器 理解词嵌入&#xff1a;给词语画"…...

Python如何给视频添加音频和字幕

在Python中&#xff0c;给视频添加音频和字幕可以使用电影文件处理库MoviePy和字幕处理库Subtitles。下面将详细介绍如何使用这些库来实现视频的音频和字幕添加&#xff0c;包括必要的代码示例和详细解释。 环境准备 在开始之前&#xff0c;需要安装以下Python库&#xff1a;…...

基于Java Swing的电子通讯录设计与实现:附系统托盘功能代码详解

JAVASQL电子通讯录带系统托盘 一、系统概述 本电子通讯录系统采用Java Swing开发桌面应用&#xff0c;结合SQLite数据库实现联系人管理功能&#xff0c;并集成系统托盘功能提升用户体验。系统支持联系人的增删改查、分组管理、搜索过滤等功能&#xff0c;同时可以最小化到系统…...

【Android】Android 开发 ADB 常用指令

查看当前连接的设备 adb devices 连接设备 adb connect 设备IP 断开已连接的设备 adb disconnect 设备IP 安装应用 adb install 安装包的路径 卸载应用 adb uninstall 应用包名 查看已安装的应用包名 adb shell pm list packages 查看已安装的第三方应用包名 adb shell pm list…...

Caliper 负载(Workload)详细解析

Caliper 负载(Workload)详细解析 负载(Workload)是 Caliper 性能测试的核心部分,它定义了测试期间要执行的具体合约调用行为和交易模式。下面我将全面深入地讲解负载的各个方面。 一、负载模块基本结构 一个典型的负载模块(如 workload.js)包含以下基本结构: use strict;/…...

用鸿蒙HarmonyOS5实现中国象棋小游戏的过程

下面是一个基于鸿蒙OS (HarmonyOS) 的中国象棋小游戏的实现代码。这个实现使用Java语言和鸿蒙的Ability框架。 1. 项目结构 /src/main/java/com/example/chinesechess/├── MainAbilitySlice.java // 主界面逻辑├── ChessView.java // 游戏视图和逻辑├──…...

​​企业大模型服务合规指南:深度解析备案与登记制度​​

伴随AI技术的爆炸式发展&#xff0c;尤其是大模型&#xff08;LLM&#xff09;在各行各业的深度应用和整合&#xff0c;企业利用AI技术提升效率、创新服务的步伐不断加快。无论是像DeepSeek这样的前沿技术提供者&#xff0c;还是积极拥抱AI转型的传统企业&#xff0c;在面向公众…...