C++初阶-list类的模拟实现

list类的模拟实现

  • 一、基本框架
    • 1.1 节点类
    • 1.2 迭代器类
    • 1.3 list类
  • 二、构造函数和析构函数
    • 2.1 构造函数
    • 2.2 析构函数
  • 三、operator=的重载和拷贝构造
    • 3.1 operator=的重载
    • 3.2 拷贝构造
  • 四、迭代器的实现
    • 4.1 迭代器类中的各种操作
    • 4.1 list类中的迭代器
  • 五、list的增容和删除
    • 5.1 尾插尾删
    • 5.2 头插头删
    • 5.3 任意位置的插入和删除
  • 六、完整代码
    • 6.1 list.h
    • 6.2 test.cpp

一、基本框架

list的底层和vector和string不同,实现也有所差别,特别是在迭代器的设计上,本文讲介绍list的简单模拟实现。
list底层是一个带头双向循环链表,在节点上变化不大。
list整体由三个类组成
1.节点类(封装一个节点)
2.迭代器类
3.list类

1.1 节点类

list节点类是一个模板类以适合于任何类型的ist对象,封装了一个节点需要的基本成员
成员变量
前驱指针 _prev
后继指针 _next
数据存储_data

成员函数
只有一个构造函数,用于初始化节点data数据和置空指针,构造函数的x参数是缺省参数,适应不同场景。
注意节点类不需要拷贝构造和析构函数,对于节点的拷贝构造在list中只需要浅拷贝即可,节点的释放也不在节点类中进行!

template<class T>
struct list_node
{list_node<T>* _next;list_node<T>* _prev;T _data;
};

1.2 迭代器类

  迭代器类也是封装节点,但是迭代器类不会构造节点,只会利用现有节点构造一个迭代器,在迭代器内部通过各种运算符重载对节点的状态做修改!
  其次,会涉及返回节点指针或数据data的引用,所以需要在构造对象时将list数据类型的指针和引用类型传给迭代器对象!
  迭代器在不访问data的情况下返回的都是迭代器,将声明本迭代器类型self,在函数操作完成时返回self(迭代器)即可!

//1、迭代器要么就是原生指针
//2、迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为
template<class T,class Ref,class Ptr>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T,Ref,Ptr> self;node* _node;
};

迭代器对象不会凭空创造节点,只会利用现有的节点(迭代器节点类型与list节点类型相同),所以不需要拷贝构造和析构函数,浅拷贝足以!

1.3 list类

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;
};

二、构造函数和析构函数

2.1 构造函数

节点类的构造
将节点进行初始化

template<class T>
struct list_node
{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x=T()):_next(nullptr),_prev(nullptr),_data(x){}
};

迭代器的构造
成员变量只有结点node,对结点进行初始化

template<class T,class Ref,class Ptr>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T,Ref,Ptr> self;node* _node;__list_iterator(node* n):_node(n){}
};

list类的构造
因为是带头的链表,所以在实例化时必须事先申请一个头结点,所以所有的构造函数都会在操作前申请一个头结点,为了避免代码的冗余,我们将头节点的创建封装为一个函数,在构造函数初始化时调用创建头结点。
当我们需要构造空链表时,直接调用empty_init();函数即可
迭代器区间构造也是调用push_back进行尾插

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;void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}list(){/*_head = new node;_head->_next = _head;_head->_prev = _head;*/empty_init();}template <class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);++first;}}private:node* _head;
};

2.2 析构函数

析构函数需要释放所有结点以及头结点,在释放前需要判断当前链表是否为空,如果为空直接置空头节点

~list()
{clear();delete _head;_head = nullptr;
}	void clear()
{iterator it = begin();while (it != end()){//it = erase(it);erase(it++);}
}

三、operator=的重载和拷贝构造

3.1 operator=的重载

对于赋值重载,与拷贝构造相似,但是赋值重载在传递参数时使用传值传参,这样就自动帮我们构造了一个临时对象,我们只需要swap一下临时对象的头节点即可,将我们现在的链表交给临时对象销毁,这样就完成了赋值。有时可能需要连等,所以需要返回对象的引用。

//lt1=lt3
list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

3.2 拷贝构造

拷贝构造最好采用现代写法,构造临时对象使用swap交换,在此之前,因为是构造函数所以需要创造头结点,调用empty_init()函数,再进行拷贝

//lt2(lt1) 拷贝构造传统写法
/*list(const list<T>& lt)
{empty_init();for (auto e : lt){push_back(e);}
}*/void swap(list<T>& tmp)
{std::swap(_head, tmp._head);
}//现代写法
list(const list<T>& lt)
{empty_init();list<T> tmp(lt.begin(), lt.end());swap(tmp);
}

四、迭代器的实现

4.1 迭代器类中的各种操作

const类型的对象只能使用const类型的迭代器,那么const类型的迭代器如何实现呢、需要再重新封装吗,像上面那样。可以,但是没有必要,因为那样代码的冗余度就会很高,我们只需要给模板增加两个参数就可以了。即template<class T,class Ref,class Ptr>
在这里插入图片描述

//1、迭代器要么就是原生指针
//2、迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为
template<class T,class Ref,class Ptr>
struct __list_iterator
{typedef list_node<T> node;typedef __list_iterator<T,Ref,Ptr> self;node* _node;__list_iterator(node* n):_node(n){}Ref& operator*(){return _node->_data;}Ptr operator->()    //it->_a1  it->->_a1  本来应该是两个->,但是为了增强可读性,省略了一个->{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& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}
};

4.1 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;//typedef __list_const_iterator<T> const_iterator;iterator begin(){//iterator it(_head->_next);//return it;return iterator(_head->_next);}const_iterator begin() const{//iterator it(_head->_next);//return it;return const_iterator(_head->_next);}iterator end(){//iterator it(_head);//return it;return iterator(_head);}const_iterator end() const{//iterator it(_head);//return it;return const_iterator(_head);}

五、list的增容和删除

5.1 尾插尾删

尾插结点需要修改_head和tail的连接关系,链入新节点
1.根据参数创建一个新节点new_node
2.记录原尾结点tail
3.修改_head和tail之间的链接关系,再其中链入new_node
在这里插入图片描述

直接复用insert即可

void push_back(const T& x=T())
{/*node* tail = _head->_prev;node* new_node = new node(x);tail->_next = new_node;new_node->_prev = tail;new_node->_next = _head;_head->_prev = new_node;*/insert(end(), x);
}

直接复用erase即可

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

5.2 头插头删

直接复用insert即可

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

直接复用erase即可

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

5.3 任意位置的插入和删除

任意位置插入是在pos迭代器位置前插入一个结点,并返回这个结点的迭代器!
1.检查pos迭代器中结点但是否正常
2.获取pos位置的当前结点pos._node,获取pos位置的前驱结点cur->_prev
3.根据参数申请一个新节点new_node
4.将新节点new_node链入pos结点与pos前驱结点之间
5.链入成功后返回新插入结点的迭代器

在这里插入图片描述

iterator insert(iterator pos, const T& x)
{assert(pos._node);node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;return iterator(new_node);
}

在这里插入图片描述
任意位置删除时删除pos迭代器位置的结点,并返回删除结点的下一个结点的迭代器
1.检查链表是否为空且pos迭代器中结点是否正常
2.获取pos结点的前驱结点pos._node->_prev
3.获取pos结点的后继结点pos._node->_next
4.链接pos._node->_prev和pos._node->_next结点,剥离pos结点
5.删除pos结点
6.返回pos._node->_next结点的迭代器(即pos的下一个结点的迭代器)

iterator erase(iterator pos)
{assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);
}

六、完整代码

6.1 list.h

#pragma once
#include<assert.h>namespace zl
{template<class T>struct list_node{list_node<T>* _next;list_node<T>* _prev;T _data;list_node(const T& x=T()):_next(nullptr),_prev(nullptr),_data(x){}};//1、迭代器要么就是原生指针//2、迭代器要么就是自定义类型对原生指针的封装,模拟指针的行为template<class T,class Ref,class Ptr>struct __list_iterator{typedef list_node<T> node;typedef __list_iterator<T,Ref,Ptr> self;node* _node;__list_iterator(node* n):_node(n){}Ref& operator*(){return _node->_data;}Ptr operator->()    //it->_a1  it->->_a1  本来应该是两个->,但是为了增强可读性,省略了一个->{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& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};/*template<class T>struct __list_const_iterator{typedef list_node<T> node;typedef __list_const_iterator<T> self;node* _node;__list_const_iterator(node* n):_node(n){}const T& 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& s){return _node != s._node;}bool operator==(const self& s){return _node == s._node;}};*/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;//typedef __list_const_iterator<T> const_iterator;iterator begin(){//iterator it(_head->_next);//return it;return iterator(_head->_next);}const_iterator begin() const{//iterator it(_head->_next);//return it;return const_iterator(_head->_next);}iterator end(){//iterator it(_head);//return it;return iterator(_head);}const_iterator end() const{//iterator it(_head);//return it;return const_iterator(_head);}void empty_init(){_head = new node;_head->_next = _head;_head->_prev = _head;}list(){/*_head = new node;_head->_next = _head;_head->_prev = _head;*/empty_init();}template <class Iterator>list(Iterator first, Iterator last){empty_init();while (first != last){push_back(*first);++first;}}//lt2(lt1) 拷贝构造传统写法/*list(const list<T>& lt){empty_init();for (auto e : lt){push_back(e);}}*/void swap(list<T>& tmp){std::swap(_head, tmp._head);}//现代写法list(const list<T>& lt){empty_init();list<T> tmp(lt.begin(), lt.end());swap(tmp);}//lt1=lt3list<T>& operator=(list<T> lt){swap(lt);return *this;}~list(){clear();delete _head;_head = nullptr;}void clear(){iterator it = begin();while (it != end()){//it = erase(it);erase(it++);}}void push_back(const T& x=T()){/*node* tail = _head->_prev;node* new_node = new node(x);tail->_next = new_node;new_node->_prev = tail;new_node->_next = _head;_head->_prev = new_node;*/insert(end(), x);}void push_front(const T& x = T()){insert(begin(), x);}void pop_back(){erase(--end());}void pop_front(){erase(begin());}void insert(iterator pos, const T& x){node* cur = pos._node;node* prev = cur->_prev;node* new_node = new node(x);prev->_next = new_node;new_node->_prev = prev;new_node->_next = cur;cur->_prev = new_node;}iterator erase(iterator pos){assert(pos != end());node* prev = pos._node->_prev;node* next = pos._node->_next;prev->_next = next;next->_prev = prev;delete pos._node;return iterator(next);}private:node* _head;};void print_list(const list<int>& lt){list<int>::const_iterator it = lt.begin();while (it != lt.end()){//(*it) *= 2;cout << *it << " ";++it;}cout << endl;}void test_list1(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);list<int>::iterator it = lt.begin();while (it != lt.end()){cout << *it << " ";++it;}cout << endl;for (auto e : lt){cout << e << " ";}cout << endl;print_list(lt);}struct AA{int _a1;int _a2;AA(int a1=0,int a2=0):_a1(a1),_a2(a2){}};void test_list2(){list<AA> lt;lt.push_back(AA(1, 1));lt.push_back(AA(2, 2));lt.push_back(AA(3, 3));//AA* ptrlist<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;}void test_list3(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;auto pos = lt.begin();++pos;lt.insert(pos, 20);for (auto e : lt){cout << e << " ";}cout << endl;lt.push_back(100);lt.push_front(1000);for (auto e : lt){cout << e << " ";}cout << endl;lt.pop_back();lt.pop_front();for (auto e : lt){cout << e << " ";}cout << endl;}void test_list4(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;lt.clear();for (auto e : lt){cout << e << " ";}cout << endl;lt.push_back(10);lt.push_back(20);lt.push_back(30);lt.push_back(40);for (auto e : lt){cout << e << " ";}cout << endl;}void test_list5(){list<int> lt;lt.push_back(1);lt.push_back(2);lt.push_back(3);lt.push_back(4);for (auto e : lt){cout << e << " ";}cout << endl;list<int> lt2(lt);for (auto e : lt2){cout << e << " ";}cout << endl;list<int> lt3;lt3.push_back(10);lt3.push_back(20);lt3.push_back(30);lt3.push_back(40);lt3.push_back(50);for (auto e : lt3){cout << e << " ";}cout << endl;lt = lt3;for (auto e : lt){cout << e << " ";}cout << endl;}
}

6.2 test.cpp

#define _CRT_SECURE_NO_WARNINGS 1
#include<iostream>
#include<vector>
#include<list>
using namespace std;
#include "list.h"int main()
{//zl::test_list1();//zl::test_list2();//zl::test_list3();//zl::test_list4();zl::test_list5();//std::vector<int>::iterator it;//cout << typeid(it).name() << endl;return 0;
}

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

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

相关文章

javacv的视频截图功能

之前做了一个资源库的小项目&#xff0c;因为上传资源文件包含视频等附件&#xff0c;所以就需要时用到这个功能。通过对视频截图&#xff0c;然后作为封面缩略图&#xff0c;达到美观效果。 首先呢&#xff0c;需要准备相关的jar包&#xff0c;之前我用的是低版本的1.4.2&…

Tomcat-安装部署(源码包安装)

一、简介 Tomcat 是由 Apache 开发的一个 Servlet 容器&#xff0c;实现了对 Servlet 和 JSP 的支持&#xff0c;并提供了作为Web服务器的一些特有功能&#xff0c;如Tomcat管理和控制平台、安全域管理和Tomcat阀等。 简单来说&#xff0c;Tomcat是一个WEB应用程序的托管平台…

基于Nexus搭建Maven私服基础入门

什么是Nexus&#xff1f;它有什么优势&#xff1f; 要了解为什么需要nexus的存在&#xff0c;我们不妨从以下几个问题来简单了解一下: 为什么需要搭建私服&#xff1f;如果没有私服会出现什么问题&#xff1f; 对于企业开发而言&#xff0c;如果没有私服&#xff0c;我们所有…

十九)Stable Diffusion使用教程:ai室内设计案例

今天我们聊聊如何通过SD进行室内设计装修。 方式一:controlnet的seg模型 基础起手式: 选择常用算法,抽卡: 抽到喜欢的图片之后,拖到controlnet里: 选择seg的ade20k预处理器,点击爆炸按钮,得到seg语义分割图,下载下来: 根据语义分割表里的颜色值,到PS里进行修改: 语…

SoloLinker第一次使用记录,解决新手拿到板子的无所适从

本文目录 一、简介二、进群获取资料2.1 需要下载资料2.2 SDK 包解压 三、SDK 编译3.1 依赖安装3.2 编译配置3.3 启动编译3.4 编译后的固件目录 四、固件烧录4.1 RV1106 驱动安装4.2 打开烧录工具4.3 进入boot 模式&#xff08;烧录模式&#xff09;4.4 烧录启动固件4.5 烧录升级…

大型网站架构演进过程

架构演进 大型网站的技术挑战主要来自于庞大的用户&#xff0c;高并发的访问和海量的数据&#xff0c;任何简单的业务一旦需要处理数以P计的数据和面对数以亿计的用户&#xff0c;问题就会变得很棘手。大型网站架构主要就是解决这类问题。 架构选型是根据当前业务需要来的&…

RRC下的NAS层

无线资源控制&#xff08;Radio Resource Control&#xff0c;RRC&#xff09;&#xff0c;又称为无线资源管理&#xff08;RRM&#xff09;或者无线资源分配&#xff08;RRA&#xff09;&#xff0c;是指通过一定的策略和手段进行无线资源管理、控制和调度&#xff0c;在满足服…

数据库 02-03 补充 SQL的子查询(where,from),子查询作为集合来比较some,exists,all(某一个,存在,所有)

子查询&#xff1a; where字句的子查询&#xff1a; 通常用in关键字&#xff1a; 举个例子&#xff1a; in关键字&#xff1a; not in 关键字&#xff1a; in 也可以用于枚举集合&#xff1a; where中可以用子查询来作为集合来筛选元祖。 some&#xff0c;all的运算符号…

Spring cloud - 断路器 Resilience4J

其实文章的标题应该叫 Resilience4J&#xff0c;而不是Spring Cloud Resilience4J&#xff0c;不过由于正在对Spring cloud的一系列组件进行学习&#xff0c;为了统一&#xff0c;就这样吧。 概念区分 首先区分几个概念 Spring cloud 断路器&#xff1a;Spring Cloud的官网对…

elementui + vue2实现表格行的上下移动

场景&#xff1a; 如上&#xff0c;要实现表格行的上下移动 实现&#xff1a; <el-dialogappend-to-bodytitle"条件编辑":visible.sync"dialogVisible"width"60%"><el-table :data"data1" border style"width: 100%&q…

Flutter在Visual Studio Code上首次创建运行应用

一、创建Flutter应用 1、前提条件 安装Visual Studio Code并配置好运行环境 2、开始创建Flutter应用 1)、打开Visual Studio Code 2)、打开 View > Command Palette。 3)、在搜索框中输入“flutter”&#xff0c;弹出内容如下图所示&#xff0c;选择“ Flutter: New Pr…

12345、ABCDE项目符号列表文字视频怎么制作?重点内容介绍PR标题模板项目工程文件

Premiere模板&#xff0c;包含10个要点标题12345、ABCDE项目符号列表文字模板PR项目工程文件。可以根据自己的需要定制颜色。在视频的开头、中间和结尾使用。包括视频教程。 适用软件&#xff1a;Premiere Pro 2019 | 分辨率&#xff1a;19201080 (HD) | 文件大小&#xff1a;9…

Visual Studio 2022封装C代码为x64和x86平台动态库

1.引言 本文介绍如何使用Visual Studio 2022将C语言函数封装成x64和x86平台上使用的动态链接库(dll文件)并生成对应的静态链接库(lib文件)&#xff0c;以及如何在C程序中调用生成的dll。 程序下载&#xff1a; 2.示例C语言程序 假设需要开发一个动态链接库&#xff0c;实现复…

CleanMyMac X2024(Mac优化清理工具)v4.14.5中文版

CleanMyMac X是一款颇受欢迎的专业清理软件&#xff0c;拥有十多项强大的功能&#xff0c;可以进行系统清理、清空废纸篓、清除大旧型文件、程序卸载、除恶意软件、系统维护等等&#xff0c;并且这款清理软件操作简易&#xff0c;非常好上手&#xff0c;特别适用于那些刚入手苹…

提升英语学习效率,尽在Eudic欧路词典 for Mac

Eudic欧路词典 for Mac是一款专为英语学习者打造的强大工具。无论您是初学者还是高级学习者&#xff0c;这款词典都能满足您的需求。 首先&#xff0c;Eudic欧路词典 for Mac具备丰富的词库&#xff0c;涵盖了各个领域的单词和释义。您可以轻松查询并学习单词的意思、用法和例…

数据泄露警报:不同行业危机解析与迅软DSE的拯救之道

在如今全球信息数字化不断加速的时代里&#xff0c;数据资料的价值更为突出&#xff0c;根据IBM数据显示&#xff0c;数据泄露的平均成本接近440万美元。一旦泄露可能意味着丢失信息、声誉受损&#xff0c;并可能导致延误和生产力损失。那么不同行业一旦发生了数据泄露将会面临…

大 O 表示法在机器学习中的重要性

一、介绍 在不断发展的机器学习领域&#xff0c;算法的效率至关重要。大 O 表示法成为这方面的一个关键工具&#xff0c;它提供了一种描述算法性能或复杂性的语言&#xff0c;特别是在时间和空间方面。本文探讨了 Big O 表示法在机器学习中的重要性&#xff0c;阐明了它在算法选…

狗dog目标检测数据集VOC+YOLO格式1W+张

狗&#xff0c;是食肉目犬科 [11]犬属 [13]哺乳动物 [12]&#xff0c;别称犬&#xff0c;与马、牛、羊、猪、鸡并称“六畜” [13]。狗的体型大小、毛色因品种不同而不同&#xff0c;体格匀称&#xff1b;鼻吻部较长&#xff1b;眼呈卵圆形&#xff1b;两耳或竖或垂&#xff1b;…

一文搞懂OSI参考模型与TCP/IP

OSI参考模型与TCP/IP 1. OSI参考模型1.1 概念1.2 数据传输过程 2. TCP/IP2.1 概念2.2 数据传输过程 3. 对应关系4. 例子4.1 发送数据包4.2 传输数据包4.3 接收数据包 1. OSI参考模型 1.1 概念 OSI模型&#xff08;Open System Interconnection Reference Model&#xff09;&a…

复制粘贴——QT实现原理

复制粘贴——QT实现原理 QT 剪贴板相关类 QClipboard 对外通用的剪贴板类&#xff0c;一般通过QGuiApplication::clipboard() 来获取对应的剪贴板实例。 // qtbase/src/gui/kernel/qclipboard.h class Q_GUI_EXPORT QClipboard : public QObject {Q_OBJECT private:explici…