[C++]:12:模拟实现list

[C++]:12:模拟实现list

  • 一.看一看SGI的stl_list的源码:
    • 1.基础结构+构造函数
      • 1.节点结构:
      • 2.节点构造函数:
      • 3.链表结构:
      • 4.链表的构造函数:
    • 2.析构
      • 1.节点析构:
      • 2.链表的析构:
    • 3.迭代器
  • 二.模拟实现list
    • 1.基础结构+构造函数:
      • 1.节点:
      • 2.链表:
      • 3.实现迭代器+遍历数据:
        • 1.迭代器实现:
        • 3.数据遍历(可读可写)
        • 4.数据遍历(只读)
      • 3.拷贝构造+赋值
    • 2.增
      • 1.insert
      • 2.push_front && push_back
    • 3.删
      • 1.erase
      • 2.pop_front && pop_back
    • 4.查
      • 1.find
    • 5.改:
      • 2.amend
    • 6.析构函数:
      • 1.clear 和 ~list
    • 7.容量相关的函数:
      • 1.size()
      • 2.empty()
    • 8.实现operator->的意义:
      • 1.简单解决问题的方法:
      • 2.实现operator->
      • 3.一个问题:

一.看一看SGI的stl_list的源码:

1.基础结构+构造函数

1.节点结构:

在这里插入图片描述

1.SGI下的节点通过两个结构体实现。
2.基础的链表节点只包括前驱指针和后继指针。
3.链表节点去继承基础链表节点,新增节点数据。
4.优化掉指针类型带模板参数。

2.节点构造函数:

1.节点本身在这个地方是没有构造函数的。
2.观察下面的链表的结构和链表的构造函数可以观察出一点细节。

3.链表结构:

1._last_base类的构造通过_M_get_node方法进行节点的空间分配。
2.初始化一个基础的链表需要构造一个哨兵位的头节点。
3.开始的时候让哨兵位的头节点自己指向自己构造一个双向带头循环的一个结构。
4.list类继承_list_base类的时候里面多了许多的typedef

在这里插入图片描述
请添加图片描述

4.链表的构造函数:

1.通过上面的代码我们发现我们构造一个节点并没有通过节点的构造函数进行构造。
2.在list类型中提供一个方法去在插入新的节点的时候去调用。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

2.析构

1.节点析构:

在这里插入图片描述

1.使用了内存池去回收节点的空间。

2.链表的析构:

在这里插入图片描述

3.迭代器

1.类比string或者vector他们的迭代器就是原生指针是比较方便的。
2.重写operator++ 和 operator*
3.对于节点来说结构不同于string和vector的。
4.参考SGI的源码发现对于内置类型是不可以实现运算符的重载。
5.实现一个迭代器的类型!

在这里插入图片描述

在这里插入图片描述

二.模拟实现list

1.基础结构+构造函数:

1.节点:

1.自己模拟实现就不这么复杂。
2.sgi通过内存池获取空间通过_creat_node get_node函数去对新增节点的创建。
3.节点自己把自己的数据在内部调整好的构造函数。
4.insert这样的函数去处理定义节点的问题。

//1.节点结构template<class T>struct ListNode {//1.节点的构造函数:ListNode(T x){date = x;}//1.类+模板-->具体的类型。ListNode<T>* prev=nullptr;ListNode<T>* next=nullptr;T date;};

2.链表:

//3.链表结构template<class T>class list{public://1.构造:双向带头循环链表的基本结构!list():head(new ListNode<T>(T())){head->prev = head;head->next = head;}private:ListNode<T>* head;};
}

3.实现迭代器+遍历数据:

1.内置类型没有办法进行运算符的重载。
2.迭代器本质就是节点的指针。
3.把一个节点指针类型包裹在一个自定义类型中。
4.在list和iterator_ListNode类中都对相应的类型进行了重定义。

1.迭代器实现:
template<class T>struct itreator_ListNode {//2.提供迭代器的方法:typedef itreator_ListNode<T> self;typedef ListNode<T> Node;Node* _node;itreator_ListNode(Node* x):_node(x){}//1.运算符重载:bool operator!=(self x){return this->_node != x._node;}bool operator==(self x){return this->_node == x._node;}//2.运算符重载++ --self& operator++(){this->_node = this->_node->next;return *this;}self operator++(int){self ret = *this;this->_node = this->_node->next;return ret;}self& operator--(){this->_node = this->_node->prev;return *this;}self operator--(int){self ret = *this;this->_node = this->_node->prev;return ret;}T& operator*(){return this->_node->date;}};
	template<class T>class list{public://1.构造:双向带头循环链表的基本结构!list():head(new ListNode<T>(T())){head->prev = head;head->next = head;}//2.提供迭代器的方法:typedef itreator_ListNode<T> iterator;typedef ListNode<T> Node;//2-1:迭代器应该满足左闭右开//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!iterator begin() { return head->next; }iterator end() {return head;}void push_back(T x){//1.产生一个节点:ListNode<T>* newnode = new ListNode<T>(x);//2.进行节点的连接:ListNode<T>* prev = head->prev;prev->next = newnode;newnode->prev = prev;head->prev = newnode;newnode->next = head;}private:Node* head;};
3.数据遍历(可读可写)

在这里插入图片描述

4.数据遍历(只读)

1.提供const迭代器。
2.注意:const迭代器并不是迭代器本身是const类型,如果迭代器本身是const类型不能实现operator++ 或者operator–。
3.有两个方法去实现const迭代器!

方法一:定义一个新的类:

//提供一个新的类template<class T>struct iterator_const_ListNode {//2.提供迭代器的方法:typedef iterator_const_ListNode<T> self;typedef ListNode<T> Node;Node* _node;iterator_const_ListNode(Node* x):_node(x){}//1.运算符重载:bool operator!=(self x){return this->_node != x._node;}bool operator==(self x){return this->_node == x._node;}//2.运算符重载++ --self& operator++(){this->_node = this->_node->next;return *this;}self operator++(int){self ret = *this;this->_node = this->_node->next;return ret;}self& operator--(){this->_node = this->_node->prev;return *this;}self operator--(int){self ret = *this;this->_node = this->_node->prev;return ret;}const T& operator*(){return this->_node->date;}/*self& operator=(self x){this = x;}*/};

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

方法二:使用模板参数表示不同类型(泛型编程)

1.我们通过上面多去实现一个iterator_const_ListNode去实现const迭代器。
2.问题:代码冗余。
3.实现const迭代器就是实现一个const T& operator*返回类型的不同!

//2.节点封装-支持正向迭代器template<class T , class Ref>struct iterator_ListNode {//2.提供迭代器的方法:typedef iterator_ListNode<T,Ref> self;typedef ListNode<T> Node;Node* _node;iterator_ListNode(Node* x):_node(x){}//1.运算符重载:bool operator!=(self x){return this->_node != x._node;}bool operator==(self x){return this->_node == x._node;}//2.运算符重载++ --self& operator++(){this->_node = this->_node->next;return *this;}self operator++(int){self ret = *this;this->_node = this->_node->next;return ret;}self& operator--(){this->_node = this->_node->prev;return *this;}self operator--(int){self ret = *this;this->_node = this->_node->prev;return ret;}Ref operator*(){return this->_node->date;}/*self& operator=(self x){this = x;}*/};
//2.提供迭代器的方法:typedef iterator_ListNode<T,T&> iterator;typedef iterator_ListNode<T,const T&> const_iterator;typedef ListNode<T> Node;//2-1:迭代器应该满足左闭右开//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!iterator begin() { return head->next; }iterator end() {return head;}const_iterator cbegin() { return head->next; }const_iterator cend() { return head; }

3.拷贝构造+赋值

//1.拷贝构造:list(list& copy):head(new ListNode<T>(T())){head->prev = head;head->next = head;//循环copy调用push_backfor (auto num : copy){push_back(num);}}
//赋值相关+交换函数void swap(list& tmp){Node* head = this->head;this->head = tmp.head;tmp.head = head;}list operator=(list tmp){swap(tmp);return *this;}

2.增

1.insert

在这里插入图片描述

1.模拟实现第一个insert函数提供迭代器和插入的节点数据:

//为什么不可以iterator&类型返回//Node* 类型到iterator类型通过单参数的构造函数支持的:发生隐式类型转换!//Node* 类型到iterator&类型没有被支持的!iterator insert(iterator pos , T x = T()){//1.产生一个节点:Node* newnode = new ListNode<T>(x);//2.连接!Node* next = pos._node->next;pos._node->next = newnode;newnode->prev = pos._node;newnode->next = next;next->prev = newnode;return newnode;}

2.push_front && push_back

	void push_back(T x = T()){insert(head->prev, x);}void push_front(T x = T()){insert(head, x);}

3.删

1.erase

在这里插入图片描述

//2.删除考虑返回一下下一个位置的迭代器:iterator erase(iterator pos){Node* prev = pos._node->prev;Node* next = pos._node->next;prev->next = next;next->prev = prev;//1.使用默认生成的析构函数delete pos._node;return next;}

2.pop_front && pop_back

void pop_back(){erase(head->prev);}void pop_front(){erase(head->next);}

4.查

1.find

iterator find(T x){iterator cur = begin();while (cur != end()){if (cur._node->date == x)return cur;cur = cur._node->next;//单参数构造函数支持的隐式类型转换!}return nullptr;}

5.改:

2.amend

//修改数据:void amend(iterator pos,T x){pos._node->date = x;}

6.析构函数:

在这里插入图片描述

1.clear 和 ~list

1.清除所有的节点数据会保留头节点。
2.使用clear后的状态应该满足只有一个哨兵位的头节点并且前驱指向自己后继指向自己。

//4.遍历链表清理节点:void clear(){Node* cur = head->next;while (cur != head){Node* next = cur->next;delete cur;cur = next;}head->next = head;head->prev = head;}//析构:~list(){clear();delete head;}

7.容量相关的函数:

1.size()

//容量相关:size_t size(){assert(head != nullptr);int count = 0;if (empty())return 0;else{iterator cur = begin();while (cur != end()){count++;cur = cur._node->next;//单参数构造函数支持的隐式类型转换!}return count;}}

2.empty()

bool empty(){assert(head != nullptr);if (head->next == head)return true;return false;}

8.实现operator->的意义:

在这里插入图片描述

主要思路:
1.实现了一个自定义类型AB。
2.push_back多个匿名AB类型的对象到链表l1中。
3.通过迭代器遍历链表数据。
4.因为我们没有去重载AB类型的operator流插入,所以不可以正常的打印数据。

1.简单解决问题的方法:

在这里插入图片描述

2.实现operator->

1.我们知道->结构体指针类型的对象去访问成员变量的一个方法。
2.实现operator->要比对一个类型去实现operator<< operator>>方便。
3.通过->访问AA数据考虑const还是非const的?
4.给模板再加上一个参数去确定operator->返回值类型。

template<class T , class Ref , class arrows>struct iterator_ListNode {//2.提供迭代器的方法:typedef iterator_ListNode<T,Ref,arrows> self;typedef ListNode<T> Node;Node* _node;iterator_ListNode(Node* x):_node(x){}//1.运算符重载:bool operator!=(self x){return this->_node != x._node;}bool operator==(self x){return this->_node == x._node;}//2.运算符重载++ --self& operator++(){this->_node = this->_node->next;return *this;}self operator++(int){self ret = *this;this->_node = this->_node->next;return ret;}self& operator--(){this->_node = this->_node->prev;return *this;}self operator--(int){self ret = *this;this->_node = this->_node->prev;return ret;}Ref operator*(){return this->_node->date;}arrows operator->(){return &(this->_node->date);}/*self& operator=(self x){this = x;}*/};
//2.提供迭代器的方法:typedef iterator_ListNode<T,T&, T* > iterator;typedef iterator_ListNode<T,const T&,const T*> const_iterator;typedef ListNode<T> Node;//2-1:迭代器应该满足左闭右开//List_Node<T>* 类型到iterator类型是通过单参数的构造函数支持的!iterator begin() { return head->next; }iterator end() {return head;}const_iterator cbegin() { return head->next; }const_iterator cend() { return head; }

在这里插入图片描述
在这里插入图片描述

3.一个问题:

在这里插入图片描述

1.在这个地方去访问数据的时候应该是两个箭头。
2.第一个箭头是重载的operator->的箭头。
3.第二个箭头是返回T或者constT 去访问数据的箭头。
4.为什么只通过一个箭头就访问到数据了呢?

在这里插入图片描述

1.按照我们之前的理解方法二是没有任何问题我们想要去掉operator按照之前的理解应该转化成方法三。
2.方法三为什么是错误的呢?
3.因为我们需要提供可读性所以我们让编译器去做了操作优化成了方法一的这样的语法。
4.总结:理论上应该优化为方法三但是为了可读性所以优化为了方法一:编译器承担了一切。

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

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

相关文章

vue+elementUI el-select 中 没有加clearable出现一个或者多个×清除图标问题

1、现象&#xff1a;下方截图多清除图标了 2、在全局common.scss文件中加一个下方的全局样式noClear 3、在多清除图标的组件上层div加noClear样式 4、清除图标去除成功

基于扩散模型语音驱动人物头像说话模型:DreamTalk

1 DreamTalk介绍 DreamTalk&#xff1a;由清华大学、阿里巴巴和华中科大共同开发的一个基于扩散模型让人物头像说话的框架。 能够根据音频让人物头像照片说话、唱歌并保持嘴唇的同步和模仿表情变化。这一框架具有以下特点: DreamTalk能够生成高质量的动画&#xff0c;使人物脸…

【操作系统】实验一 Linux操作系统安装

&#x1f57a;作者&#xff1a; 主页 我的专栏C语言从0到1探秘C数据结构从0到1探秘Linux &#x1f618;欢迎关注&#xff1a;&#x1f44d;点赞&#x1f64c;收藏✍️留言 &#x1f3c7;码字不易&#xff0c;你的&#x1f44d;点赞&#x1f64c;收藏❤️关注对我真的很重要&…

Nginx的access_log 状态码499的问题排查

前提&#xff1a;公司的项目网站&#xff0c;运行环境是lnmp环境下 一、起因 如下图&#xff0c;网站请求超过60s(如&#xff1a;导出半年的报表数据到excel)时&#xff0c;报如下错误&#xff0c;且浏览器上没有返回值 二、发展 查找nginx和php-fpm都没有报错日志。于是先把…

C++中命名空间、缺省参数、函数重载

目录 1.命名空间 2.缺省参数 3.函数重载 1.命名空间 在C中定义命名空间我们需要用到namespace关键字&#xff0c;后面跟上命名空间的名字&#xff0c;结构框架有点类似结构体&#xff08;如图所示&#xff09; 上面的代码我一一进行讲解&#xff1a; 1.我们先来说第三行和main函…

MySql必知必会

11.什么是自适应哈希索引&#xff1f; 自适应Hash索引&#xff08;Adatptive Hash Index&#xff0c;内部简称AHI&#xff09;是InnoDB的三大特性之一&#xff0c;还有两个是 Buffer Pool简称BP、双写缓冲区&#xff08;Doublewrite Buffer&#xff09;。 1、自适应即我们不需…

网络防御保护1

网络防御保护 第一章 网络安全概述 网络安全&#xff08;Cyber Security&#xff09;是指网络系统的硬件、软件及其系统中的数据受到保护&#xff0c;不因偶然的或者恶意的原因而遭受到破坏、更改、泄露&#xff0c;系统连续可靠正常地运行&#xff0c;网络服务不中断 随着数…

今年想考CISP的一定要看完❗️

&#x1f3af;国家注册信息安全专业人员&#xff08;英文名称Certified Information Security Professional&#xff0c;简称“CISP"&#xff09;&#xff0c;是由中国信息安全测评中心于2002年推出的、业内公认的国内信息安全领域zqw的gj级认证&#xff0c;是国家对信息安…

华为欧拉操作系统结合内网穿透实现固定公网地址SSH远程连接

文章目录 1. 本地SSH连接测试2. openEuler安装Cpolar3. 配置 SSH公网地址4. 公网远程SSH连接5. 固定连接SSH公网地址6. SSH固定地址连接测试 欧拉操作系统(openEuler, 简称“欧拉”)是面向数字基础设施的操作系统,支持服务器、云计算、边缘openEuler是面向数字基础设施的操作系…

记录昆仑通态:HMI

目录 基本图元应用&#xff1a; 标签构件应用&#xff1a;​编辑 位图构件应用&#xff1a;​编辑 输入框构件应用&#xff1a;​编辑 流动块构件应用&#xff1a;​编辑 百分比填充构件应用&#xff1a;​编辑 标准按钮构件应用&#xff1a;​编辑 动画按钮构件应用&…

Armv8-M的TrustZone技术之内存属性单元

如果处理器包含Armv8-M安全扩展&#xff0c;则内存区域的安全状态由内部安全属性单元&#xff08;SAU&#xff0c;Secure Attribution Unit&#xff09;或外部实现定义的属性单元&#xff08;IDAU&#xff0c;Implementation Defined Attribution Unit&#xff09;的组合控制。…

JavaScript库jquery的使用方法

"写更少&#xff0c;做更多"是jquery的设计理念&#xff0c;jquery是一个兼容多浏览器的JavaScript库&#xff0c;利用jquery的语法设计能使开发更便捷。 网页添加jquery的方法:1.从jquery.com下载库&#xff1b;2.从CDN中载入库&#xff08;示例使用&#xff09;&a…