C++:list增删查改模拟实现

news2024/9/25 13:14:25

C++:list增删查改模拟实现

  • 前言
  • 一、list底层双链表验证、节点构造
    • 1.1 list底层数据结构
    • 1. 2 节点构造
  • 二、迭代器封装实现(重点、难点)
    • 2.1 前置说明
    • 2.2 迭代器实现
  • 三、list实现
    • 3.1 基本框架
    • 3.2 迭代器和const迭代器
    • 3.2 构造函数、析构函数、拷贝构造、赋值重载
      • 3.2.1 构造函数
      • 3.2.2 析构函数
      • 3.2.3 拷贝构造
      • 3.2.4 赋值重载
    • 3.3 任意位置插入、任意位置删除、尾插、尾删、头插、头删
    • 3.3.1任意位置插入
    • 3.3.2任意位置插入删除
    • 3.3.3 尾插、尾删、头插、头删
  • 四、list功能完善
    • 4.1 迭代器operator->()
    • 4.2 打印数据
  • 五·、所有代码以及测试用例

前言

本篇博客采用SGI版本,同时考虑到看到此篇博客大部分为初学者,为此博主仅仅给出有用片段。C++:list增删查改模拟实现就是用C++复写双链表,非常简单。难点主要在迭代器实现

一、list底层双链表验证、节点构造

1.1 list底层数据结构

list底层使用什么数据结构来实现的呢?我们先来看看SGI库中list的成员函数和初始化吧。
在这里插入图片描述

我们发现list实现中,只有node一个成员变量。构造函数构造出一个头尾相连的哨兵位。同时验证了list底层是一个带哨兵位的双链表。

1. 2 节点构造

节点和双链表一样有三个成员:节点数据、指向前一个节点(prev)、指向后一个节点(next)。

//节点
template<class T>
struct List_node
{
	T _data;
	List_node<T>* _prev;
	List_node<T>* _next;

	List_node(const T& x = T())
		:_data(x)
		,_prev(nullptr)
		,_next(nullptr)
	{}
};

小tips:

  1. 我们这里类名和库中一样(List_node),后续在其他类中使用时在typedef。
  2. 这里类名使用struct而不是class原因在于struct默认访问权限为公有,后续其他类只都需要大量使用。如果使用class需要使用大量友元类,过于繁琐。

二、迭代器封装实现(重点、难点)

2.1 前置说明

  1. 我们知道迭代器的最大用途便是遍历数据。但何时停在,迭代器指向空间存储数据时是什么…导致我们需要使用!=、++、*等操作。但自定义类型是无法支持使用这些操作符的。为此给出的解决办法是:封装加运算符重载
  2. 迭代器使用上分为普通迭代器和const迭代器(还分为单向迭代器、双向迭代器和随机访问迭代器)。其中一种最简单的实现方式就是实现两个类。但。。。我们知道两个迭代器的不同之处在于const迭代器无法修改数据,只是j相关借口(这里有*、->)不同而已便实现两个类未免过于“小题大做”。
    所以接下来我们来看看库中是如何巧妙的解决这个问题吧!
    在这里插入图片描述

2.2 迭代器实现

前置说明中以及解决了如何实现一个类达到目的。接下来的实现过于简单就不单独说明了。

//迭代器封装
template<class T, class Ref, class Ptr>
struct __list_iterator
{
	typedef List_node<T> Node;//节点类名重命名
	//由于迭代器实现中,如++、--等重载函数返回值类型为__list_iterator,名字过长,这里我们重命名self意味自身
	typedef __list_iterator<T,Ref, Ptr> self;
	Node* _node;//成员变量

	__list_iterator(Node* node)//构造出一个节点
		:_node(node)
	{}

	//前置++
	self& operator++()
	{
		_node = _node->_next;
		return *this;
	}
	//前置--
	self& operator--()
	{
		_node = _node->_prev;
		return *this;
	}
	
	//后置++
	self operator++(int)
	{
		self tmp(*this);
		_node = _node->_next;
		return tmp;
	}
	//后置--
	self operator--(int)
	{
		self tmp(*this);
		_node = _node->_prev;
		return tmp;
	}
	
	//解引用操作符重载
	Ref operator*()
	{
		return _node->_data;
	}
	
	//用于访问迭代器指向对象中存储的是自定义类型
	Ptr operator->()
	{
		return &_node->_data;
	}

	bool operator!=(const self& s)
	{
		return _node != s._node;
	}

	bool operator==(const self& s)
	{
		return _node == s._node;
	}
};

三、list实现

3.1 基本框架

list模拟中,我们和库中一样,给出一个头节点_head、_size两个成员变量。同时我们将节点、迭代器进行重命名。迭代器重命名不是单纯图方便,更重要的是提供统一接口(例如sting、vector、set等接口都一样),屏蔽底层的变量和成员函数属性,实现过程和差异。

//list模拟实现
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;
private:
	Node* _head;//头节点
	int _size;
};

3.2 迭代器和const迭代器

下面是begin()、end()指向一个有效双线表的位置。
在这里插入图片描述

实现:

const_iterator begin()const
{
	//return const_iterator(_head->_next);或
	return _head->_next;//单参数类型支持隐式类型转换
}

const_iterator end()const
{
	return _head;
}

iterator begin()
{
	return _head->_next;
}

iterator end()
{
	return _head;
}

3.2 构造函数、析构函数、拷贝构造、赋值重载

3.2.1 构造函数

构造函数的实现和开头中看到的一样:构造函数中调用一个函数(empty_Init)来是实现。empty_Init()用来完成初始化。(empty_Init()函数后续其他接口也要调用)

//初始化
void empty_Init()
{
	_head = new Node;
	_head->_next = _head;
	_head->_prev = _head;

	_size = 0;
}

//无参构造
List()
{
	empty_Init();
}

3.2.2 析构函数

析构函数我们调用一个clear函数来将数据全部清空,在将_head变量释放。

//析构函数
~List()
{
	clear();
	delete _head;
	_head = nullptr;
}

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

3.2.3 拷贝构造

拷贝构造时首先要初始化出一个节点,然后将需要拷贝的数据依次尾插即可(尾插接口后续给出实现)

//拷贝构造
List(const List<T>& It)
{
	empty_Init();
	for (auto e : It)
	{
		push_back(e);
	}
}

3.2.4 赋值重载

赋值重载有很多方式。比较简单的参数我们直接传值,然后将待赋值的变量和传值传参省生成的临时变量的数据进行交换即可。

void swap(const List<T>& It)
{
	std::swap(_head, It._head);
}

//赋值重载
List<T>& operator=(const List<T> It)
{
	swap(It);
	return *this;
}

3.3 任意位置插入、任意位置删除、尾插、尾删、头插、头删

3.3.1任意位置插入

首先new出待插入节点newnode,然后将pos前一个节点prev、newnode、pos相连。最后++_size即可。
在这里插入图片描述

iterator insert(iterator pos, const T& x)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* newnode = new Node(x);

	prev->_next = newnode;
	newnode->_prev = prev;

	newnode->_next = cur;
	cur->_prev = newnode;
	_size++;
	return newnode;
}

3.3.2任意位置插入删除

将待删除数据的前后节点先保存起来,然后将删除pos处节点,再将前后节点连接起来。最后–_size即可。

iterator erase(iterator pos)
{
	Node* cur = pos._node;
	Node* prev = cur->_prev;
	Node* next = cur->_next;
	delete cur;
	prev->_next = next;
	next->_prev = prev;

	_size--;
	return next;
}

3.3.3 尾插、尾删、头插、头删

尾插、尾删、头插、头删都是复用任意位置插入、任意位置删除接口。

void push_back(const T& x)
{
	insert(end(), x);
}

void push_front(const T& x)
{
	insert(begin(), x);
}

void pop_back()
{
	erase(--end());
}

void pop_front()
{
	erase(begin());
}

四、list功能完善

4.1 迭代器operator->()

我们先来看看下面这段代码对吗?

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

	int _a1;
	int _a2;
};

void test_list3()
{
	list<AA> lt;
	lt.push_back(AA(1, 1));
	lt.push_back(AA(2, 2));
	lt.push_back(AA(3, 3));

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

由于list没有重载<<,所以对存储的是自定义类型之间访问会编译报错。
那我们重载下<<运算符不就行了吗?很不幸的是C++库在list中不支持<<,很大原因也在于编译器不知到我们如何取数据

所以对于自定义类型我们可以先解引用在去访问成员,也可以在迭代器中重载operator->()函数。但需要注意的是编译器隐藏了一个->,具体原生行为如下:

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

	int _a1;
	int _a2;
};

void test_list3()
{
	list<AA> lt;
	lt.push_back(AA(1, 1));
	lt.push_back(AA(2, 2));
	lt.push_back(AA(3, 3));

	list<AA>::iterator it = lt.begin();
	while (it != lt.end())
	{
		//cout << (*it)._a1<<" "<<(*it)._a2 << endl;
		cout << it->_a1 << " " << it->_a2 << endl;
		//上面编译器访问成员变量的真正行为如下:
		//cout << it.operator->()->_a1 << " " << it.operator->()->_a1 << endl;
		++it;
	}
	cout << endl;
}

4.2 打印数据

//大多数情况模板是class还是typename是一样的,但当有未实例化模板时,必须使用typename
//template<typename T>
void print_list(const list<T>& lt)
{
	// list<T>未实例化的类模板,编译器不能去他里面去找
	// 编译器就无法list<T>::const_iterator是内嵌类型,还是静态成员变量
	// 前面加一个typename就是告诉编译器,这里是一个类型,等list<T>实例化
	// 再去类里面去取
	typename list<T>::const_iterator it = lt.begin();
	while (it != lt.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;  
}

优化:上面打印数据是针对list,下面是针对容器的。

// 模板(泛型编程)本质,本来应该由我们干的活交给编译器
template<typename Container>
void print_container(const Container& con)
{
	typename Container::const_iterator it = con.begin();
	while (it != con.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

五·、所有代码以及测试用例

giteeC++:list增删查改模拟实现代码以及测试用例
推荐:giteeC++:list增删查改模拟实现代码(最终版本、感觉版本!!!)

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

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

相关文章

解决FTP传输慢的问题(ftp传输慢为什么)

在企业运营中&#xff0c;使用FTP进行文件或数据传输是相当普遍的做法。尽管FTP是一种传统的文件传输工具&#xff0c;但在实际应用中&#xff0c;我们可能会面临传输速度缓慢的问题&#xff0c;这不仅影响工作效率&#xff0c;还浪费时间。为了解决这一问题&#xff0c;我们可…

Vue中Render函数、_ref属性、_props配置的使用

Render函数 由于导入的vue为vue.runtime.xxx.js是运行版的vue.只包含&#xff1a;核心功能&#xff1a;没有模板解析器 完整版的Vue为vue.js包含&#xff1a;核心功能模板解析器 vue.runtime.esm.js中的esm为ES6的模块化 //导入的vue并非完整的vue&#xff0c;这样做的好处是…

实战:朴素贝叶斯文本分类器搭建与性能评估

&#x1f497;&#x1f497;&#x1f497;欢迎来到我的博客&#xff0c;你将找到有关如何使用技术解决问题的文章&#xff0c;也会找到某个技术的学习路线。无论你是何种职业&#xff0c;我都希望我的博客对你有所帮助。最后不要忘记订阅我的博客以获取最新文章&#xff0c;也欢…

Druid源码阅读-DruidStatInterceptor实现

上次我们在druid-spring-boot-starter里面看到有一个DruidSpringAopConfiguration的配置类&#xff0c;然后引入了DruidStatInterceptor这样一个切面逻辑。今天我们就来看一下这个类的实现。 DruidStatInterceptor 这个类的包路径下入com.alibaba.druid.support.spring.stat。…

服务器代码上传到gitlab

服务器代码上传到gitlab 安装Git工具linux&#xff1a;安装Git&#xff0c;使用自带的源安装 yum install git生成密钥文件&#xff1a;使用ssh-keygen生成密钥文件.ssh/id_rsa.pub ssh-keygen 使用cat命令查看密钥&#xff0c;将下面的密钥复制一份 在gitlab上建立一个和…

2023年12月16日~12月22日(自适应反馈机制下基于卷积神经网络的高清晰反射波反演算法:CNN-RWI)

标题&#xff1a;Adaptive Feedback Convolutional-Neural-Network-Based High-Resolution Reflection-Waveform Inversion 全波形反演&#xff08;FWI&#xff09;是一种非线性拟合观测地震记录从而获得高清晰速度模型的最优化算法。FWI能够通过拟合浅层初至波和反射波获得较准…

浅学JWT跨域认证

Json Web令牌简称JWT 由HeaderPayloadSignature组成 Header JWT头是一个描述JWT元数据的JSON对象&#xff0c;alg属性表示签名使用的算法&#xff0c;默认为HMAC SHA256&#xff08;写为HS256&#xff09;&#xff1b;typ属性表示令牌的类型&#xff0c;JWT令牌统一写为JWT。…

湘沪数字产业(上海)协同创新中心正式启动!

前言 随着全球化的加速和市场竞争的日益激烈&#xff0c;产业协作已成为推动科技创新和经济发展的重要手段。在创新驱动战略的推动下&#xff0c;全国形成了在科创核心城市建设科创中心的浪潮&#xff0c;旨在充分利用不同区域的产业优势&#xff0c;加强产业协作&#xff0c;…

BFS解决FloodFill算法相关leetcode算法题

文章目录 1.图像渲染2.岛屿数量3.岛屿的最大面积4.被围绕的区域 1.图像渲染 图像渲染 class Solution {int dx[4] {0,0,1,-1};int dy[4] {1,-1,0,0}; public:vector<vector<int>> floodFill(vector<vector<int>>& image, int sr, int sc, int…

计算机视觉技术-使用图像增广进行训练

让我们使用图像增广来训练模型。 这里&#xff0c;我们使用CIFAR-10数据集&#xff0c;而不是我们之前使用的Fashion-MNIST数据集。 这是因为Fashion-MNIST数据集中对象的位置和大小已被规范化&#xff0c;而CIFAR-10数据集中对象的颜色和大小差异更明显。 CIFAR-10数据集中的前…

【MIMO 从入门到精通】[P4]【MIMO Communications】

前言&#xff1a; Explains the main approaches to multi-input multi-output (MIMO) communications, including Beamforming, Zero Forcing, and MMSE. * Note that at the 9:19min mark, I made a slight "voice typo", where I should have said: "you nee…

AI进化太快了!Stability AI开源视频生成大模型Stable Video Diffusion

对于 Stable Diffusion&#xff0c;想必读者朋友们对此都不陌生。 自 Stability AI 公司发布 SD&#xff08;全称&#xff1a;Stable Diffusion) 以来&#xff0c;受到了很多人的喜爱。 SDXL 效果 随后技术升级&#xff0c;又发布了 SDXL&#xff0c;名字很有喜感&#xff0c…

计网04-网络传输介质

物理层&#xff08;网卡、传输介质&#xff09; 一、信号 &#xff11;、概念 进行网络通信在线缆中传输的就是信号&#xff0c;网线传输电信号&#xff0c;光纤传输光信号。 信息&#xff1a;对现实事物存在的某种认识数据&#xff1a;描述某些属性的具体的量子&#xff0…

C/C++学习笔记十三 C++中的重载运算符

1、什么是运算符重载&#xff1f; 运算符重载是 C 中的一项功能&#xff0c;使运算符&#xff08;例如 、- 等&#xff09;能够处理用户定义的数据类型。这种机制称为编译时多态性&#xff0c;并提供了为不同数据类型定制运算符行为的优点。 例如&#xff0c;我们可以重载“”运…

基于SpringBoot实现一个可扩展的事件总线

基于SpringBoot实现一个可扩展的事件总线 前言 在日常开发中&#xff0c;我们经常会用到事件总线&#xff0c;SpringBoot通过事件多播器的形式为我们提供了一个事件总线&#xff0c;但是在开发中我们经常会用到其他的实现&#xff0c;比如Guava、Disruptor的。我们将基于Spri…

迪杰斯特拉算法详解

迪杰斯特拉算法详解 首先要知道的是&#xff0c;迪杰斯特拉算法是求解单源最短路径的&#xff0c;就是在一个图中&#xff08;无向图和有向图均可&#xff09;&#xff0c;指定一个源点&#xff0c;求出来这个源点到其他各个节点的最短路径。 存图 首先&#xff0c;我需要用…

HarmonyOS应用兼容稳定性云测试

兼容性测试 兼容性测试主要验证HarmonyOS应用在华为真机设备上运行的兼容性问题&#xff0c;包括首次安装、再次安装、启动、卸载、崩溃、黑白屏、闪退、运行错误、无法回退、无响应、设计约束场景。具体兼容性测试项的详细说明请参考兼容性测试标准。 兼容性测试支持TV、智能穿…

爬虫系列----Python解析Json网页并保存到本地csv

Python解析JSON 1 知识小课堂1.1 爬虫1.2 JSON1.3 Python1.4 前言技术1.4.1 range1.4.2 random1.4.3 time.sleep1.4.4 with open() as f: 2 解析过程2.1 简介2.2 打开调试工具2.3 分析网址2.3.1 网址的规律2.3.2 网址的参数 2.4 爬取第一页内容2.5 存入字典并获取2.6 循环主体数…

DolphinScheduler 介绍及系统架构

目录 一、DolphinScheduler 介绍 1.1 关于 DolphinScheduler 1.2 特性 简单易用 丰富的使用场景 High Reliability High Scalability 1.3 名词解释 1.3.1 名词解释 1.3.2 模块介绍 二、DolphinScheduler 系统架构 2.1 系统架构图 2.2 架构说明 MasterServer 该服…