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;//链表大小};
}

总结


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

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

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

相关文章

计算机视觉与深度学习实战,Python工具,多尺度形态学提取眼前节

一、引言 在医疗影像诊断领域,眼前节图像的准确分析对于眼科疾病的诊断至关重要。近年来,随着计算机视觉和深度学习的快速发展,越来越多的研究者开始尝试利用这些技术来辅助医疗影像的解读。其中,多尺度形态学作为一种有效的图像处理工具,在眼前节图像的分割和特征提取中展…

g++ 预处理 编译 汇编 链接 命令

g 预处理 编译 汇编 链接 命令 在命令行中使用 g 预处理、编译、汇编和链接源代码文件通常遵循以下步骤&#xff1a; 预处理&#xff08;Preprocessing&#xff09;&#xff1a;将源代码文件转换为经过预处理器处理的中间文件。 g -E source.cpp -o source.i 编译&#xff…

柴油十六烷值检测 液压油硫含量检测 变压器油检测

检测的油品包括&#xff1a;柴油、润滑油、液压油、机油、汽油、以及一些工业用油等。 柴油检测项目GB19147-2013&#xff1a;氧化安定性、硫含量、酸度、10%蒸余物残炭、灰分、铜片腐蚀、水分、机械杂质、润滑性、多环芳烃、运动粘度、凝点、冷滤点、闪点、十六烷值、馏程、密…

什么是Docker ?

在软件开发的星辰大海中&#xff0c;有一个神奇的技术&#xff0c;它能够将应用程序及其依赖环境封装在一个轻量级的、可移植的容器中。这项技术就是Docker。它不仅简化了应用的部署流程&#xff0c;还让开发和运维之间的界限变得模糊&#xff0c;使得跨平台部署变得前所未有的…

如何搭建跨境电商独立站||搭建跨境电商独立站必须具备的功能板块设计

在搭建跨境电商独立站时&#xff0c;需要确保网站具备一系列关键的功能板块&#xff0c;以提供用户友好的购物体验并确保业务的顺利进行。以下是这些功能板块的详细归纳&#xff1a; 注册登录与身份验证&#xff1a; 用户注册与登录&#xff1a;允许用户创建账户&#xff0c;通…

52.Fork Join线程池

介绍 jdk1.7之后加入的新的线程池的实现。 实现一种分治的思想。 适用于能够进行任务拆分的cpu密集型运算。 任务拆分 将一个大任务拆分为算法上相同的小任务,直至不能拆分可以直接求解。例如跟递归相关的一些计算,如归并排序、斐波那契数列都可以利用分治的思想。 For…

动态规划学习(混合背包,有依赖的背包,以及背包思想)

混合背包的定义&#xff1a; 混合背包问题就是混合01背包、完全背包和多重背包&#xff0c;可供选择的物体i可能有一个、或者无数个、或者有限个。 所以&#xff0c;就不要考虑这么多了&#xff0c;直接分这三种情况考虑就行&#xff01;&#xff01; 样例&#xff1a; for(…

CW32F030K8T7单片机在即热式热水器的应用介绍

随着智能家居技术的不断进步&#xff0c;即热式热水器作为现代家庭中的重要组成部分&#xff0c;正逐渐向智能化、节能化方向发展。本方案通过采用武汉芯源半导体的CW32F030系列单片机&#xff0c;以其高性能、超强抗干扰等特性&#xff0c;为即热式热水器的智能化提供了理想的…

Allegro导入DXF文件

阿里狗导入DXF文件 点击File–>Import–>DXF&#xff0c;注意DXF file那边不能使用中文路径和文件名以及非法字符&#xff0c;DXF units一般为mm&#xff0c;结构那边一般都用mm制作图&#xff0c;右边三个选项只需要勾选中间那个&#xff0c;意思是以增加的形式导入&am…

AI图书推荐:这就是ChatGPT

这本书《这就是ChatGPT》&#xff08;What Is ChatGPT Doing ... and Why Does It Work &#xff09;由Stephen Wolfram撰写 全书内容概要如下&#xff1a; **引言与预备知识** - 作者首先表达了对ChatGPT技术突破的兴奋之情&#xff0c;指出这不仅是技术的故事&#xff0c;也是…

FastAPI给docs/配置自有域名的静态资源swagger-ui

如果只是要解决docs页面空白的问题&#xff0c;可先看我的这篇博客&#xff1a;FastAPI访问/docs接口文档显示空白、js/css无法加载_fastapi docs打不开-CSDN博客 以下内容适用于需要以自用域名访问swagger-ui的情况&#xff1a; 1. 准备好swagger-ui的链接&#xff0c;如&am…

【机器学习】专业名词解释 202107090086

数据&#xff1a; 在机器学习中&#xff0c;数据是指机器学习算法的输入和输出。数据可以是各种类型的信息&#xff0c;如文本、图像、音频、视频、传感器数据等。数据在机器学习中扮演着非常重要的角色&#xff0c;因为它是训练模型和进行预测的基础。数据集&#xff1a; 数据…

Vue2工程化

本节目标 工程化开发项目运行流程组件化组件注册自定义创建项目 工程化开发 基于构建工具的环境开发Vue Webpack的缺点 webpack的配置并不简单基础的配置雷同各公司缺乏统一标准 Vue CLI Vue CLI是Vue官方提供的一个全局命令工具帮助我们快速创建标准化的开发环境( 集成了w…

设计模式-设计模式分类

概述 23 种设计模式&#xff0c;分为创建型模式、结构型模式和行为型模式。另外&#xff0c;近来这一清单又增加了一些类别&#xff0c;例如&#xff0c;并发型模式、线程池模式、Java EE 企业技术的多层应用程序上的模式等。 一、创建型模式 1.工厂方法模式(Factory Method…

Tensorflow音频分类

tensorflow https://www.tensorflow.org/lite/examples/audio_classification/overview?hlzh-cn 官方有移动端demo 前端不会 就只能找找有没有java支持 注意版本 注意JDK版本 package com.example.demo17.controller;import org.tensorflow.*; import org.tensorflow.ndarra…

2024年5月文章一览

2024年5月编程人总共更新了7篇文章&#xff1a; 1.2024年4月文章一览 2.《自动机理论、语言和计算导论》阅读笔记&#xff1a;p215-p351 3.《自动机理论、语言和计算导论》阅读笔记&#xff1a;p352-P401 4.《自动机理论、语言和计算导论》阅读笔记&#xff1a;p402-p427 …

05 Linux 内核启动流程

1、阅读 Linux 内核源码 学习 Linux 有两种路线: 1)按照 Linux 启动流程,梳理每个子系统。 2)把 Linux 所有用到的子系统学会,再组合起来。 博主选择第一种方式,可以快速上手,知道自己在学什么东西,在什么阶段起作用。 阅读 Linux 和 Android 源码: https://elix…

2013.8.5-2024.5.10碳排放权交易明细数据

2013.8.5-2024.5.10碳排放权交易明细数据 1、时间&#xff1a;2013.8.5-2024.5.10 2、来源&#xff1a;各碳排放交易所 3、范围&#xff1a;各交易所城市 4、指标&#xff1a;行政区划代码、地区、所属省份、交易日期、交易品种、开盘价_元、最高价_元、最低价_元、成交均价…

列举Spring的IoC和AOP的特点

Spring框架的IoC&#xff08;Inversion of Control&#xff0c;控制反转&#xff09;和AOP&#xff08;Aspect-Oriented Programming&#xff0c;面向切面编程&#xff09;是两个核心特性&#xff0c;它们各自具有显著的特点。 IoC&#xff08;控制反转&#xff09;的特点 解耦…

杂项——编码器控制小车走固定距离(stm32)

先算出轮子转一周编码器的数值&#xff08;假设为1000&#xff09;&#xff0c;再算出轮子一周的周长&#xff08;假设为10cm&#xff09;。 那么要前进1米只需要转1米/10cm等于10圈&#xff0c;10圈编码器数值为10000。 我们只需要在代码里面写出编码器数值如果<10000则让…