CD46.【C++ Dev】list的模拟实现(1)

目录

1.STL库的list

2.模拟实现

节点结构体

list类

无参构造函数

尾插函数

迭代器★

begin()

operator++

前置++

后置++

operator--

前置--

后置--

operator!=

operator==

end()

operator*

const修饰的迭代器的设计


1.STL库的list

模拟实现list之前,先看看STL库里的list是怎么做的

STL v3.0版本代码一览:

先看链表的结构体和构造函数

结构体:

template <class T>
struct __list_node {typedef void* void_pointer;void_pointer next;//后指针void_pointer prev;//前指针T data;//数据域
};

看到next和prev,显然是双向链表,复习双向链表的参见:

96.【C语言】数据结构之双向链表的初始化,尾插,打印和尾删
97.【C语言】数据结构之双向链表的头插,头删,查找,中间插入,中间删除和销毁函数

CC32.【C++ Cont】静态实现双向链表及STL库的list

 构造函数:

list() { empty_initialize(); }//调用空链表的构造函数
void empty_initialize() { node = get_node();//调用空间配置器node->next = node;node->prev = node;
}//......
protected:link_type node;

2.模拟实现

定义节点结构体next和prev成员变量,不建议用void_pointer,因为void*需要强制类型转换,比较麻烦

节点结构体

仿照STL:

namespace mystl
{template<class T>struct __list_node{typedef __list_node<T>* link_type;link_type next;link_type prev;T data;};//......
}

list类

框架: 

template<class T>
class list
{typedef __list_node<T> list_node;
public:
private:
};

发现STL库中的成员变量只有一个

node为__list_node<T>的指针,显然是双向链表的哨兵位,如下图 

照葫芦画瓢写:

template<class T>
class list
{typedef __list_node<T> list_node;typedef __list_node<T>* link_type;
public:
private:link_type node;
};

无参构造函数

生成哨兵位,让next和prev都指向自己

list()
{node = new list_node;node->next = node;node->prev = node;
}

测试代码:

#include "list.h"
int main()
{mystl::list<int> ls;return 0;
}

下断点到return 0,看看无参构造函数是否能正常工作

 可以正常工作

尾插函数

void push_back(const T& x)
{link_type tmp = new list_node;tmp->data = x;//先找尾link_type tail = node->prev;tail->next = tmp;tmp->prev = tail;tmp->next = node;node->prev = tmp;
}

测试代码:

#include "list.h"
using namespace std;
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);return 0;
}

运行结果:

当然上面的代码可以简写

link_type tmp = new list_node(x);

new list_node(x)会自动调用list_node的构造函数,需要提前准备,将list_node构造函数写在struct list_node中即可

struct __list_node
{typedef __list_node<T>* link_type;__list_node(const T& x = T())//写成缺省参数的形式:next(nullptr), prev(nullptr), data(x){}link_type next;link_type prev;T data;
};

迭代器★

迭代器的本质:自定义类型的封装

list迭代器不能像模拟vector一样是指针,因为list不是连续区间,因此迭代器需要重新定义,能让迭代器++能直接指向下一个节点

因此:vector<int>::iterator和list<int>::iterator从封装上的格式上来看是一样的,但底层的实现不一样

即封装自定义指针成对象,符合C++面向对象的思想

//it是迭代器,本质上都是调用类的成员函数
it.operator++();
it.operator++(0);
it.operator--();
it.operator--(0);
it.operator*();
it.operator!=(...);
it.operator==(...);

STL库的解决方法:写成结构体,存储节点的指针

struct __list_iterator
{link_type node;
};

 (省略了成员函数和typedef)

再自定义成iterator

typedef __list_iterator<T> iterator;

 可以发现迭代器定义成了实例化的类

注意:__list_iterator定义成类时不要定义到list里面,嵌套类(可参见文章)是有限制的!

template<class T>
struct __list_iterator//是类
{typedef __list_node<T>* link_type;link_type node;
};

 当然list类里面要定义迭代器,这样利用访问

begin()

写在list类中,可以直接构造iterator对象返回

iterator begin()
{//返回哨兵位的下一个节点//或者写成return node->next;使用隐式类型转换return iterator(node->next);//构造对象返回
}

当然node->next是自定义类型,需要手动在__list_iterator类中添加构造函数,这个很容易忘

struct __list_iterator
{typedef __list_node<T>* link_type;__list_iterator(link_type x){node = x;}link_type node;
};

或者写成初始化列表:

__list_iterator(link_type x):node(x)
{}

测试代码:

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);//::可以访问typedef的或者访问内部类mystl::list<int>::iterator it=ls.begin();return 0;
} 

运行结果:

operator++

operator++是__list_iterator类的方法,不能写在list类中,因为list类中没有迭代器成员变量

前置++

返回++后的iterator

typedef __list_iterator<T> iterator;
iterator& operator++()
{node= node->next;return *this;
}
后置++

返回++前的iterator

iterator operator++(int)
{iterator tmp(*this);node= node->next;return tmp;
}
operator--

因为是双向链表,所以可以向前和向后移动迭代器

前置--

返回--后的iterator

iterator& operator--()
{node = node->prev;return *this;
}
后置--

返回--前的iterator

iterator& operator--(int)
{iterator tmp(*this);node = node->prev;return tmp;
}
operator!=

注意两个const修饰

const iterator& x防止权限放大,而且begin()和end()返回的是临时对象具有常性

operator!=末尾的const是防止修改node

bool operator!=(const iterator& x) const
{return node != x.node;
}
operator==
bool operator==(const iterator& x) const
{return node == x.node;
}
end()

写在list类中,由于是双向链表,因此返回哨兵位即可

iterator end()
{//返回哨兵位return node;//隐式类型转换,完整写法:iterator(node)
}

测试代码:用迭代器遍历

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);mystl::list<int>::iterator it=ls.begin();while (it != ls.end()){	std::cout << it.node->data << " ";++it;}return 0;
}

运行结果:

operator*

虽然这里定义的迭代器是实例化的对象,但是迭代器任务是模拟指针行为,因此要有operator*

T& operator*()
{return node->data;
}

 测试代码

#include <iostream>
#include "list.h"
int main()
{mystl::list<int> ls;ls.push_back(1);ls.push_back(2);ls.push_back(3);mystl::list<int>::iterator it = ls.begin();std::cout << *it;return 0;
}

注:mystl::list<int>::iterator it = ls.begin();会自动调用默认的拷贝构造,由于__list_iterator的成员变量是自定义类型的指针,浅拷贝没有问题,而且__list_iterator 不用写析构函数,节点应该是list类释放,迭代器只是借助节点访问

运行结果:

const修饰的迭代器的设计

能这样写吗?

typedef const __list_iterator<T> const_iterator;

不可以, const修饰的是 __list_iterator<T>这个类,类的成员函数不能被修改

一个简明的例子说明:

class myclass
{
public:int* node;int data;
};int main()
{const myclass obj;obj.node++;
}

obj.node++是不被允许的,被const修饰的对象的成员变量是不能被修改的

要明确: 无论是否有const修饰,双向迭代器是要能用operator++和operator--进行修改来访问的,但const修饰的迭代器指向的内容是不能被修改的,非const修饰的迭代器指向的内容能被修改

(有关const修饰的基础问题参见38.【C语言】指针(重难点)(C)文章)

那const修饰的迭代器应该模拟类似const T* ptr的情况

在迭代器的成员函数中,只有operator*需要修改,其余都不变

下文将介绍两种处理方法

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

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

相关文章

数据结构——二叉树的基本介绍

————————————本文旨在讨论与学习计算机知识&#xff0c;欢迎交流————————————上一章&#xff0c;我们讲解了树结构的综述导论&#xff0c;那么&#xff0c;现在我们来深入了解一下树结构中最常用研究的结构——二叉树结构&#xff08;上一章的扩展——…

英伟达发布 Llama Nemotron Nano 4B:专为边缘 AI 和科研任务优化的高效开源推理模型

英伟达推出了 Llama Nem)otron Nano 4B&#xff0c;这是一款专为在科学任务、编程、符号运算、函数调用和指令执行方面提供强大性能与效率而设计的开源推理模型&#xff0c;其紧凑程度足以支持边缘部署。该模型仅包含 40 亿参数&#xff0c;却在内部基准测试中实现了比其他多达…

论文阅读笔记——Autoregressive Image Generation without Vector Quantization

MAR 论文 基于 VQ&#xff08;向量量化&#xff09;的图像生成方法具有显著优势&#xff0c;它通过离散化压缩将原始图像映射到有限的 codebook 空间&#xff0c;从而缩小学习范围、降低建模难度&#xff0c;同时这种离散表示更易于与自回归&#xff08;AG&#xff09;生成方式…

【科普】关于C 语言日志系统实战:如何同时输出到终端和文件?

1.概述 c语言没有现成的日志库&#xff0c;如果要记录日志&#xff0c;需要自己封装一个日志库。如果要实现日志级别和参数打印&#xff0c;还是比较麻烦的&#xff0c;正好在github找到了一个c语言开源日志库&#xff0c;可以实现日志级别打印&#xff0c;参数打印&#xff0…

2025,数字人借直播场景迈过“真假线”丨数智化观察

作者 | 曾响铃文 | 响铃说一夜带货超5500万GMV、观看人次1300万&#xff0c;罗永浩数字人在百度电商的直播首秀正在掀起新的行业浪潮——2025&#xff0c;数字人直播带货成功出圈&#xff0c;加速进入大众视野&#xff0c;被更多的消费者所认可。成就这场热潮的关键点之一&…

HTML表格导出为Excel文件的实现方案

1、前端javascript可通过mime类型、blob对象或专业库&#xff08;如sheetjs&#xff09;实现html表格导出excel&#xff0c;适用于中小型数据量&#xff1b;2、服务器端方案利用后端语言&#xff08;如python的openpyxl、java的apache poi&#xff09;处理复杂报表和大数据&…

企业微信iPad协议端强制拉群漏洞深度分析

正常一次最多邀请40人进群 超过40人的拉群&#xff0c;会变成邀请&#xff0c;需要对方同意 新版本修复了漏洞&#xff0c;但还是可以用老版本进行强制拉群 虽然官方也做了版本过低的限制&#xff0c;但还是有办法绕过 要么修改版本号或者登录几天新版本&#xff0c;之后就可以…

Python编译器(Pycharm Jupyter)

Pycharm下载不过多赘述pycharm导入anaconda创建的python环境选择想要的环境 Jupyter Jupyter 是一个开源的交互式计算环境&#xff0c;能够让用户将代码、文本&#xff08;包括 Markdown&#xff09;、可视化结果等内容整合在一个文档中&#xff0c;非常适合进行数据分析、科学…

漏洞修复与Fiddler抓包工具的使用

漏洞描述 1. 短信轰炸漏洞 Type:存在三个不同的值。Login是登录处,register是注册账号处的短信验证码获取值,还有一个update值。未注册的用户也可以进行发送短信。 2. 手机号绕过,修改密码漏洞(逻辑漏洞) 目前注册使用手机号与忘记密码的手机号验证测试都可以绕过, …

对象存储-OSS

目录 对象存储背景 阿里云OSS 对象存储背景 单节点环境下&#xff0c;文件往往存储在tomcat服务器内&#xff0c;随着业务需求的增多&#xff0c;单节点已不能满足需求&#xff0c;项目架构需要扩展到多节点&#xff08;见下图&#xff09;&#xff0c;此时文…

C语言函数的声明

1定义&#xff1a;在C语言中&#xff0c;函数是一段具有特定功能的独立代码块&#xff0c;它可以接收输入参数、执行相关操作并返回结果。2为什么需要函数&#xff08;1&#xff09;代码复用&#xff1a;避免重复编写相同功能的代码&#xff0c; &#xff08;2&#xff09;模块…

AI人工智能名片小程序源码系统,名片小程序+分销商城+AI客服,包含完整搭建教程

智能名片核心功能AI人工智能名片小程序的核心功能设计旨在彻底改变传统商务交流方式&#xff0c;为用户提供前所未有的智能化体验。个性化名片展示是系统的基础功能&#xff0c;用户可以通过丰富的模板库和自定义设计工具&#xff0c;创建独具特色的电子名片。系统提供多种预设…

React 教程:井字棋游戏

React 教程&#xff1a;井字棋游戏 使用 React 实现一个交互式的井字棋游戏&#xff0c;并配上好看的样式 // 导入必要的CSS样式和React库 import "./App.css"; import { useState } from "react";// Square组件 - 表示棋盘上的一个格子 function Square({…

React源码2 React中的工厂函数:createRoot()

#React V18.2 源码前置基础知识&#xff1a;工厂函数工厂函数是一种设计模式&#xff0c;用于动态创建对象或函数实例。其核心思想是通过封装对象创建的细节&#xff0c;提供统一的接口&#xff0c;从而增强代码的灵活性和可维护性&#xff0c;有一些核心作用&#xff1a;解耦创…

《UE5_C++多人TPS完整教程》学习笔记42 ——《P43 瞄准(Aiming)》

本文为B站系列教学视频 《UE5_C多人TPS完整教程》 —— 《P43 瞄准&#xff08;Aiming&#xff09;》 的学习笔记&#xff0c;该系列教学视频为计算机工程师、程序员、游戏开发者、作家&#xff08;Engineer, Programmer, Game Developer, Author&#xff09; Stephen Ulibarri…

SQL Server 临时表、表变量与WITH语句的用法与区别

引言 在SQL Server数据处理中,临时表、表变量和WITH语句(CTE)是关键的中间结果集管理工具。临时表适合大数据量操作,表变量优化小数据量场景,而CTE则简化复杂查询逻辑。三者选择需综合考量数据量级、事务需求及代码可读性。本文将深入解析其工作机制,通过实测对比指导场…

【Android】组件及布局介绍

一&#xff1a;代码分析 1&#xff1a;Android界面开发方式 &#xff08;1&#xff09;JavaView&#xff08;传统视图系统&#xff09; 这是 Android 早期的开发方式&#xff0c;用 Java 或 Kotlin 代码配合 XML 布局文件 来构建界面。&#xff08;简单了解即可&#xff09; 分…

Android 音视频 IPC序列化工具-Flattenable

Android Binder与AIDL与Service使用案例及分析-CSDN博客 讲讲这个类,被用在Android音视频中,跨进程序列化反序列化用。与Binder驱动有很强的联系。位于: feameworks/native/utils/Flattenable.h Flattenable, 译为令人满意的。可能是作者十分满意自己的这些作品吧,起了这…

文献学习|全面绘制和建模水稻调控组景观揭示了复杂性状背后的调控架构。

摘要&#xff1a; 解析调控复杂性状的机制对于推进作物改良至关重要。在此&#xff0c;我们提出了一个全面的水稻&#xff08;Oryza sativa&#xff09;调控组图谱&#xff0c;涵盖了来自三个代表性品种的23种不同组织的染色质可及性。我们的研究揭示了117,176个独特的开放染色…

Linux的压缩与解压缩

一、使用tar命令进行打包与解包 1.0、tar命令简介和常用选项 tar命令是Linux中经常使用的归档工具&#xff0c;它的主要功能是【对文件或者目录进行打包归档】&#xff0c;归档为一个文件&#xff0c;但是并不进行压缩&#xff1b;tar命令的归档操作效果如下&#xff1a; tar命…