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,一经查实,立即删除!

相关文章

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

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

什么是Docker ?

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

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

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

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

混合背包的定义&#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…

Vue2工程化

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

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 …

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;行政区划代码、地区、所属省份、交易日期、交易品种、开盘价_元、最高价_元、最低价_元、成交均价…

【机器学习基础】Python编程08:五个实用练习题的解析与总结

Python是一种广泛使用的高级编程语言,它在机器学习领域中的重要性主要体现在以下几个方面: 简洁易学:Python语法简洁清晰,易于学习,使得初学者能够快速上手机器学习项目。 丰富的库支持:Python拥有大量的机器学习库,如scikit-learn、TensorFlow、Keras和PyTorch等,这些…

在线OJ项目测试(selenium+Junit5)

目录 在线OJ项目测试的思维导图 在线OJ的UI自动化测试 测试一&#xff1a;检查未登录时的页面访问以及一些未登录时的非法操作 测试二&#xff1a;测试注册界面 测试三&#xff1a;测试登录界面 测试四&#xff1a;测试题目列表界面 测试五&#xff1a;测试题目详情界面…

【python】unindent does not match any outer indentation level错误的解决办法

【Python】"unindent does not match any outer indentation level"错误的解决办法 在Python编程中&#xff0c;缩进是定义代码块的关键。与其它编程语言使用花括号或特定关键字不同&#xff0c;Python完全依赖缩进来区分代码结构。如果你在编码时遇到了错误信息unin…

【C++进阶】深入STL之list:模拟实现深入理解List与迭代器

&#x1f4dd;个人主页&#x1f339;&#xff1a;Eternity._ ⏩收录专栏⏪&#xff1a;C “ 登神长阶 ” &#x1f921;往期回顾&#x1f921;&#xff1a;初步了解 list &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; ❀STL之list &#x1f4d2;1. list…

源码、反码和补码

对于有符号数而言&#xff0c;原码就是一个数的二进制表示。二进制的最高位是符号位&#xff0c;0 表示正数&#xff0c;1 表示负数。 计算机用数的原码进行显示&#xff0c;数的计算和存储是用补码进行的。 正数的原码&#xff0c;反码和补码都一样&#xff0c;即正数三码合…

nomachine使用记录以及录包

录包命令&#xff1a; rosbag record 话题名字&#xff08;可以是原相机话题和执行程序的话题&#xff09;rosbag play 包名&#xff08;可以离线播放包的数据&#xff09; rqt_image_view 话题可视化

FileZilla:不安全的服务器,不支持 FTP over TLS 原因与解决方法

今天在用FileZilla Client连接某个主机的FTP的时候&#xff0c;主机地址、账号、密码、端口确定百分之百正确的情况下&#xff0c;结果报错如下&#xff1a; 状态: 正在解析 x.x.x 的地址 状态: 正在连接 x.x.x.x:21... 状态: 连接建立&#xff0c;等待欢迎消息... 状态: 不安全…

RHEL - 订阅、注册系统和 Yum Repository(新版界面)

《OpenShift / RHEL / DevSecOps 汇总目录》 演示环境说明 本文需要有 redhat.com 账号以及包含 RHEL 的有效订阅。 演示环境使用了通过 minimal 方式安装的 RHEL 7.6 环境&#xff0c;RHEL 可以访问互联网。 红帽网站 access.redhat.com 针对新用户提供了新版界面&#xff0…