C++:STL--List

news2025/1/11 9:10:28

文章目录

  • 一.STL-list的数据结构
        • 链表结点模板
  • 二.List的框架与迭代器的实现
      • 1.STL中的容器迭代器
      • 2.List的迭代器
        • List正向遍历迭代器类模板(==对ListNode< T >* 指针的封装==)
        • 反向遍历迭代器的类模板(==对正向迭代器的封装==)
      • 3.List的实现框架
  • 三. List的成员接口的实现
        • 1.在List类中经常被复用的接口
        • 2.List的四个构造函数重载和一个赋值运算符重载
        • 3.List容器的数据进出接口
        • 4.访问List各种存储信息的接口(以及resize接口)
        • 5.List的析构函数

在这里插入图片描述

STL容器的代码设计中,模板编程代码复用的思想贯穿始终,代码复用可以将各个成员接口统一起来从而大大增加程序的可读性和可维护性,模板编程使得容器类可以根据需要用于存储各种不同类型的数据.

一.STL-list的数据结构

C++STL标准库中的list使用的数据结构是带头双向循环链表;
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 链表的头结点不用于存储有效数据
  • 以图示中的方式设计出来的循环链表,在链表中的任意位置的插入和删除结点操作代码逻辑都是一样的,而且无须区分空表和非空表的情形,代码实现起来非常方便

链表结点模板

	template<class DataType>
	struct ListNode
	{
		ListNode(const DataType& val = DataType())
			:_prev(nullptr)
			,_next(nullptr)
			,_data(val)
		{}
		
		ListNode<DataType>* _prev;
		ListNode<DataType>* _next;
		DataType _data;
	};

二.List的框架与迭代器的实现

1.STL中的容器迭代器

STL容器的迭代器是用于访问容器中数据元素的工具,其本质上是将数据元素指针封装成类(或类模板),该类(或类模板)会对外提供成员接口(一般是运算符重载),使得迭代器对象可以像指向数组元素的普通指针一样被使用(通常支持++,- -,比较(= =,! =),解引用(*,->)等等操作)(于是遍历容器中数据元素的代码形式也变得和遍历数组元素的代码形式一致) ,从而实现容器元素遍历访问方式的高级抽象

2.List的迭代器

List正向遍历迭代器类模板(对ListNode< T >* 指针的封装)

	template <class Datatype, class Ref, class Ptr>
	class ListIterator
	{
	public:
		//告诉编译器Ptr和Ref是类型名,而不是全局变量,为实现反向迭代器做准备
		typedef Ref Ref;
		typedef Ptr Ptr;

		//简化迭代器命名,方便代码编写
		typedef ListIterator<Datatype, Ref, Ptr> Self;
		//在迭代器中实例化结点模板
		typedef ListNode<Datatype> Node;

		//构造函数
		ListIterator(Node* ptr = nullptr)
		{
			_PtrNode = ptr;
		}
		//ListIterator(const Node* ptr) 
		//{
		//	_PtrNode = ptr;
		//}
		
		//重载*运算符,返回结点数据的引用
		Ref operator*() const
		{
			return _PtrNode->_data;
		}

		//重载->,函数返回值是结点数据的地址
		//当结点数据类型仍然为自定义结构时会用到,
		Ptr operator->() const
		{
			return &(_PtrNode->_data);
		}

		//重载前置++,函数返回值是迭代器的自引用
		Self& operator++()
		{
			_PtrNode = _PtrNode->_next;
			return (*this);
		}

		//重载后置++,函数返回值是迭代器的自引用
		Self operator++(int)
		{
			Self tem(*this);
			_PtrNode = _PtrNode->next;
			return tem;
		}

		//重载前置--,函数返回值是迭代器的自引用
		Self& operator--()
		{
			_PtrNode = _PtrNode->_prev;
			return (*this);
		}

		//重载后置--,函数返回值是迭代器的自引用
		Self operator--(int)
		{
			Self tem(*this);
			_PtrNode = _PtrNode->_prev;
			return tem;
		}

		//重载比较运算符==
		bool operator==(const Self It) const
		{
			return _PtrNode == It._PtrNode;
		}

		//重载比较运算符!=
		bool operator!= (const Self It) const
		{
			return !((*this) == It);
		}
		
		//提供获取节点指针的接口
		Node* GetPtr()
		{
			return _PtrNode;
		}
		const Node* GetPtr()const
		{
			return _PtrNode;
		}
	    //成员指针
		Node* _PtrNode;
	};
  • 在迭代器中需要通过typedef对结点模板进行实例化:
    在这里插入图片描述

  • 正向迭代器模板之所以要设计三个模板参数是为了能够利用同一个迭代器模板去实例化出普通迭代器和const迭代器:

  • const迭代器是只能访问数据元素而不能修改数据元素的迭代器(一般供const对象使用)
    在这里插入图片描述

反向遍历迭代器的类模板(对正向迭代器的封装)

  • List反向迭代器是通过复用正向迭代器类模板实现的
	template<class Iterator>
	class ReverseListIterator
	{
	public:

		//告诉编译器Ref和Ptr是Iterator类域中的类型名称
		typedef typename Iterator::Ref Ref;
		typedef typename Iterator::Ptr Ptr;
		
		typedef ReverseListIterator<Iterator> Self;

		//构造函数
		ReverseListIterator(const Iterator& Rit = Iterator())
			:_Rit(Rit)
		{}

		//重载*运算符,返回结点数据的引用
		Ref operator*() const
		{
			return *_Rit;
		}

		//重载->,函数返回值是结点数据的地址
		//当结点数据类型仍然为自定义结构时会用到,
		Ptr operator->() const
		{
			return &(*_Rit);
		}

		//重载前置++,函数返回值是迭代器的自引用
		Self& operator++()
		{
			--_Rit;
			return (*this);
		}

		//重载后置++,函数返回值是迭代器的自引用
		Self operator++(int)
		{
			Self tem(*this);
			--_Rit;
			return tem;
		}

		//重载前置--,函数返回值是迭代器的自引用
		Self& operator--()
		{
			++_Rit;
			return (*this);
		}

		//重载后置--,函数返回值是迭代器的自引用
		Self operator--(int)
		{
			Self tem(*this);
			++_Rit;
			return tem;
		}

		//重载比较运算符==
		bool operator==(const Self It) const
		{
			return It._Rit== _Rit;
		}

		//重载比较运算符!=
		bool operator!= (const Self It) const
		{
			return !((*this) == It);
		}

	
		Iterator _Rit;
	};

3.List的实现框架

	template<class T>
	class List
	{
	public:
		//实例化并重命名链表结点模板
		typedef ListNode<T> Node;
		
		//实例化并重命名正向迭代器模板
		typedef ListIterator<T, T&, T*> iterator;
		typedef ListIterator<T, const T&, const T*> const_iterator;

		//实例化并重命名反向迭代器模板
		typedef ReverseListIterator<iterator> reverse_iterator;
		typedef ReverseListIterator<const_iterator> const_reverse_iterator;

		//获取迭代器的成员接口
		iterator begin()
		{
			return iterator(_Node->_next);
		}
		const_iterator begin() const
		{
			return const_iterator(_Node->_next);
		}
		iterator end()
		{
			return iterator(_Node);
		}
		const_iterator end() const
		{
			return const_iterator (_Node);
		}
		reverse_iterator rbegin()
		{
			return reverse_iterator(--end());
		}
		reverse_iterator rbegin() const
		{
			return reverse_iterator(--end());
		}
		reverse_iterator rend()
		{
			return reverse_iterator(--begin());
		}
		const_reverse_iterator rend()  const
		{
			return const_reverse_iterator(--begin());
		}





		//基本功能的成员接口
		//申请链表结点的接口
		Node* BuyListNode(const T& val = T());
		//清空List
		//采用头删删除(寻址比较方便)
		void clear();
		//交换两个list对象的内容
		void swap(List<T>& list);
	private:
		//创建头节点的私有成员接口
		void Creathead();
	public:




		//List容器的数据进出接口
		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val);
		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos);
		//链表尾插接口
		void push_back(const T& val);
		//链表尾删接口
		void pop_back();
		//链表头插接口
		void push_front(const T& val);
		//链表头删接口
		void pop_front();








		//List的构造函数
		//默认构造函数
		List();
		//构造函数模板
		//(用迭代器区间构造List对象,迭代器可以是其他类型容器的迭代器)     	
		template <class Iterator>
		List(Iterator first, Iterator last);
		//拷贝构造函数
		List(const List<T>& list);
		//创建n个T值节点的构造函数
		List(int n, const T& value = T());
		
		//List的赋值运算符重载
		List<T>& operator=(List<T> templist);

		


		//访问List各种存储信息的接口
		size_t size()const;
		//链表判空接口
		bool empty() const;
		//重新设置链表节点个数的接口
		void resize(size_t newsize, const T& data = T());

		//返回首数据元素
		T& front();
		//const重载(const修饰this指针使得const对象可以调用该接口)
		const T& front()const;
		//返回尾数据元素
		T& back();
		//const重载(const修饰this指针使得const对象可以调用该接口)
		const T& back()const;


		//List的析构函数
		~List();

	private:
		Node* _Node;
	};
};
  • List对象的成员变量只有一个结点指针,用于指向链表的头节点
  • List的框架中有五个需要实例化的模板:链表结点模板,正向迭代器模板,const正向迭代器模板,反向迭代器模板,const反向迭代器模板,创建List对象时模板的实例化过程:在这里插入图片描述

三. List的成员接口的实现

1.在List类中经常被复用的接口

  • void Creathead();创建链表头节点时注意修改头节点的指针域使其指向头节点自身:在这里插入图片描述
		void Creathead()
		{
			_Node = new Node;
			_Node->_next = _Node;
			_Node->_prev = _Node;
		}
  • void clear();清空链表的接口
		//清空List
		//采用头删删除(寻址比较方便)
		void clear()
		{
			Node* cur = _Node->_next;
			while (cur != _Node)
			{
				Node *tem = cur->_next;
				delete cur;
				cur = tem;
			}
			//注意恢复空表的指针状态
			_Node->_next = _Node;
			_Node->_prev = _Node;
		}
  • void swap(List<T>& list)交换两个链表的所有内容:实质上就是交换头节点指针的指向
		//交换两个list对象的内容
		void swap(List<T>& list)
		{
			std::swap(_Node, list._Node);
		}

2.List的四个构造函数重载和一个赋值运算符重载

		//List的构造函数
		//默认构造函数
		List()
		{
			Creathead();
		}
		//用容器迭代器区间去初始化List对象
		template <class Iterator>
		List(Iterator first, Iterator last)
		{
			Creathead();
			while (first != last)
			{
				push_back(*first);
				++first;
			}
		}
		//拷贝构造函数(复用迭代器构造函数实现)
		List(const List<T>& list)
		{
			//创建空表
			Creathead();
			List<T> tem(list.begin(), list.end());
			this->swap(tem);
		}
		//构造n个节点的链表的构造函数(复用尾插接口实现)
		List(int n, const T& value = T())
		{
			Creathead();
			while (n--)
			{
				push_back(value);
			}
		}

		//List的赋值运算符重载(复用交换函数和拷贝构造函数实现)
		List<T>& operator=(List<T> templist)
		{
			this->swap(templist);
			return (*this);
		}
  • 赋值运算符重载被调用时,函数形参也是一个List对象(存在函数栈上),因此形参压栈时调用拷贝构造函数完成被复制的对象的拷贝操作,函数返回时,templist会自动调用析构函数完成无用数据的清理

3.List容器的数据进出接口

		// 在pos位置前插入值为val的节点
		iterator insert(iterator pos, const T& val)
		{
			Node* temprve = pos._PtrNode->_prev;

			Node * NodenewNode = BuyListNode(val);
			temprve->_next = NodenewNode;
			pos._PtrNode->_prev = NodenewNode;

			NodenewNode->_next = pos._PtrNode;
			NodenewNode->_prev = temprve;

			--pos;
			return pos;
		}
		// 删除pos位置的节点,返回该节点的下一个位置
		iterator erase(iterator pos)
		{
			assert(!empty());
			Node* temprev = pos.GetPtr()->_prev;
			Node* tempnext = pos.GetPtr()->_next;

			iterator tem = pos;
			++tem;

			temprev->_next = tempnext;
			tempnext->_prev = temprev;
			delete pos.GetPtr();
			return tem;
		}
		//链表尾插接口
		void push_back(const T& val)
		{
			insert(end(), val);
		}
		//链表尾删接口
		void pop_back()
		{
			assert(!empty());
			erase(--end());
		}
		//链表头插接口
		void push_front(const T& val)
		{
			insert(begin(), val);
		}
		//链表头删接口
		void pop_front()
		{
			assert(!empty());
			erase(begin());
		}
  • 尾插,尾删,头插,头删接口都是通过接口复用的方式实现的

4.访问List各种存储信息的接口(以及resize接口)

		//获取节点个数
		size_t size()const
		{
			int count = 0;
			for (auto it : (*this))
			{
				count++;
			}
			return count;
		}
		//链表判空
		bool empty() const
		{
			return (begin() == end());
		}
		//调整链表节点个数(复用尾插尾删接口实现)
		void resize(size_t newsize, const T& data = T())
		{
			int Size = size();
			if (Size > newsize)
			{
				int Erase = Size - newsize;
				while (Erase--)
				{
					pop_back();
				}
			}
			else
			{
				int create = newsize - Size;
				while (create--)
				{
					push_back(T);
				}
			}
		}
		//获取表头数据
		T& front()
		{
			assert(!empty());
			return *begin();
		}
		const T& front()const
		{
			assert(!empty());
			return *begin();
		}
		//获取表尾数据
		T& back()
		{
			assert(!empty());
			return *(--end());
		}
		const T& back()const
		{
			assert(!empty());
			return *(--end());
		}

5.List的析构函数

  • 通过复用clear接口实现,当对象被销毁时由编译器自动调用完成内存清理工作
		~List()
		{
			clear();
			delete _Node;
			_Node = nullptr;
		}

在这里插入图片描述

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

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

相关文章

GPT-4发布!ChatGPT大升级!太太太太强了!

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 我新建了人工智能中文站https://ai.weoknow.com 每天给大家更新可用的国内可用chatGPT资源 一觉醒来&#xff0c;万众期待的GPT-4&#xff0c;它来了&#xff01; OpenAI老板Sam Altman直接开门见山地介绍说&#xff1a…

C语言之数组指针2.0

在 C 语言中&#xff0c;数组指针是一种特殊的指针类型&#xff0c;它是一个指向数组的指针。其声明形式为&#xff1a; <数据类型> (*<指针变量名>)[<数组长度>];例如&#xff0c;下面声明了一个指向整型数组的指针变量 arrPtr&#xff1a; int (*arrPtr)…

数据等级划分

数据大致可以分为定性数据与定量数据&#xff0c;但细分可以分为四类&#xff1a;定类数据、定序数据、定距数据、定比数据 处理数据的流程&#xff1a; 参考&#xff1a; 特征工程入门与实践

如何使用ChatGPT提升自己的“码”力?

如何使用chatGPT提升自己的"码"力? 代码评审(CodeReview)代码优化代码释义提供解决方案代码生成设计模式和架构建议学习新知识总结 ChatGPT是什么&#xff0c;我就不用再多介绍了吧&#xff01;相信大家已经看到了它在文本领域所展现出来的实力&#xff0c;虽然目前…

PAT A1152 Google Recruitment

1152 Google Recruitment 分数 20 作者 陈越 单位 浙江大学 In July 2004, Google posted on a giant billboard along Highway 101 in Silicon Valley (shown in the picture below) for recruitment. The content is super-simple, a URL consisting of the first 10-dig…

2023商家外卖数据

商家列表 外卖商品销量 shop_channel varchar(20) DEFAULT NULL, shop_system varchar(20) DEFAULT NULL, shop_system_no varchar(50) DEFAULT NULL, shop_platform varchar(20) DEFAULT NULL, shop_id varchar(50) NOT NULL, shop_id_str varchar(50) NO…

ChatGPT自动生成思维导图

&#x1f34f;&#x1f350;&#x1f34a;&#x1f351;&#x1f352;&#x1f353;&#x1fad0;&#x1f951;&#x1f34b;&#x1f349; ChatGPT自动生成思维导图 文章目录 &#x1f350;问题引入&#x1f350;具体操作markmapXmind &#x1f433;结语 &#x1f…

【Web】JWT(JSON Web Token)验证是什么?和SWT,SMAL的区别

JWT是什么&#xff1f; JWT&#xff08;JSON Web Token&#xff09;是一种轻量级的安全传输方式&#xff0c;可以用于在不同的系统之间传递安全可靠的信息&#xff0c;例如用户身份验证、授权和信息交换等。JWT采用JSON格式对信息进行编码和传输&#xff0c;用于在各方之间以 …

SpringCloud:分布式缓存之Redis持久化

Redis有两种持久化方案&#xff1a; RDB持久化AOF持久化 1.RDB持久化 RDB全称Redis Database Backup file&#xff08;Redis数据备份文件&#xff09;&#xff0c;也被叫做Redis数据快照。简单来说就是把内存中的所有数据都记录到磁盘中。当Redis实例故障重启后&#xff0c;…

如何安装389目录服务器作为CentOS 8 / RHEL 8机器的LDAP服务器?

LDAP&#xff08;轻量级目录访问协议&#xff09;是一种用于访问和维护分布式目录服务的开放标准协议。 389目录服务器是一个功能强大、高性能的LDAP服务器&#xff0c;它可以用于存储和管理用户、组和其他网络对象的身份验证和授权信息。本文将详细介绍如何在CentOS 8 / RHEL…

学生成绩管理系统

基于springboot vue实现的学生成绩管理系统 主要模块&#xff1a; 1&#xff09;学生模块&#xff1a;我的成绩、成绩统计、申述管理、修改密码 2&#xff09;教师模块&#xff1a;任务管理、对学生班级任务安排、班级学生的成绩查看、申述管理 3&#xff09;管理员模块&…

(可直接使用)在线语音识别APP+阿里云平台+Android Studio 开发项目

目录 所以需要下载软件 (1)Android Studio (2)夜神模拟器 1&#xff1a;在阿里云平台中&#xff0c;登录账号&#xff0c;选择控制台&#xff0c;搜索 智能语音交互 2&#xff1a;智能语音交互 界面 3:创建项目 4&#xff1a;选择项目类别 可以选择 语音识别语…

C++初识仿函数

C初识仿函数 &#x1f4df;作者主页&#xff1a;慢热的陕西人 &#x1f334;专栏链接&#xff1a;C &#x1f4e3;欢迎各位大佬&#x1f44d;点赞&#x1f525;关注&#x1f693;收藏&#xff0c;&#x1f349;留言 本博客主要内容简单介绍了仿函数的概念 文章目录 C初识仿函数…

计算机组成原理-中央处理器-CPU指令和数据执行过程

目录 一、CPU的功能和基本架构 二、指令cpu执行过程 指令周期​编辑 各周期执行过程 指令执行方案 三、数据通路 3.1 cpu内部单总线 3.1.1 寄存器之间传送 3.1.2 主存与cpu之间数据传送 3.1.3 执行算术或逻辑运算的 3.2 cpu内部多总线 3.3 专用数据通路 一、CPU的功能和…

javascript基础九:说说Javascript中的继承?如何实现继承?

一、是什么 继承&#xff08;inheritance&#xff09;是面向对象软件技术当中的一个概念 如果一个类别B“继承自”另一个类别A&#xff0c;就把这个B称为“A的子类”&#xff0c;而把A称为“B的父类别”也可以称“A是B的超类” 继承的优点 继承可以使得子类具有父类别的各种属性…

[LeetCode周赛复盘] 第 105 场双周赛20230528

[LeetCode周赛复盘] 第 105 场双周赛20230528 一、本周周赛总结6395. 购买两块巧克力1. 题目描述2. 思路分析3. 代码实现 6394. 字符串中的额外字符1. 题目描述2. 思路分析3. 代码实现 6393. 一个小组的最大实力值1. 题目描述2. 思路分析3. 代码实现 6464. 最大公约数遍历1. 题…

性能测试需求分析有哪些?怎么做?

目录 性能测试必要性评估 常见性能测试关键评估项如下&#xff1a; 性能测试工具选型 性能测试需求分析 性能测试需求评审 性能测试需求分析与传统的功能测试需求有所不同&#xff0c;功能测试需求分析重点在于从用户层面分析被测对象的功能性、易用性等质量特性&#xff…

【Go语言从入门到实战】并发篇

Go语言从入门到实战 — 并发篇 协程 Thread vs Groutine 相比之下&#xff0c;协程的栈大小就小很多了&#xff0c;创建起来也会更快&#xff0c;也更节省系统资源。 一个 goroutine 的栈&#xff0c;和操作系统线程一样&#xff0c;会保存其活跃或挂起的函数调用的本地变量…

2023 年面向初学者的 5 大自由写作技巧

在这篇文章中&#xff0c;我们将讨论初学者的自由写作技巧 译自&#xff1a;https://jitendra.co/freelance-writing-tips-for-beginners/ 比较平易近人&#xff0c;在做独立站并且自己写原创时候可以参考下&#xff0c;面对的甲方爸爸不同而已 最初的兼职活动最终成为我生活中…

Docker下安装MySQL,PostgreSQL,SQL Server(包含离线和在线安装)

1 MySQL 1.1 离线安装 1.1.1 加载镜像 使用ftp工具将安装包上传至服务器最大目录下&#xff0c;这里以根目录为最大目录举例 键入加载镜像命令&#xff1a;docker load -i 镜像包名称 docker load -i mysql80.tar使用docker images命令查看已经加载的镜像&#xff0c;如上…