STL之list

news2024/11/19 17:30:03

目录

  • list模拟实现
    • 一. list的基本框架
    • 二. list_node类
      • 1.构造函数
      • 2.其他函数
    • 三. 迭代器(iterator)
      • 1.结构
      • 2. 构造函数
      • 3. 运算符重载
          • operator->
    • 四.反向迭代器
      • 1.结构
      • 2.构造函数
      • 3.运算符重载
    • 五. list常用方法及实现
      • 1. 默认构造函数
        • a.empty_init
      • 2.迭代器
        • a. begin
        • b. end
        • c.rbegin
        • d.rend
      • 3.数据访问
        • a. size
        • b. empty
        • c. front
        • d. back
      • 4.增删操作
        • a. insert
        • b. push_back
        • c. push_front
        • d. erase
        • e. pop_front
        • f. pop_back
        • g.clear
      • 5.初始化和清理
        • a. 析构函数
        • b. n个val的构造
        • c. 迭代器区间构造
        • d. swap
        • e. 拷贝构造
        • f. operator=
    • 六.小结
      • 1.代码结构
      • 2.迭代器失效
      • 3. 反向迭代器

list模拟实现

一. list的基本框架

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

template<class T>
class list
{
    typedef list_node<T> Node;
private:
    Node* _head;
}

使用库中的list类需要包含头文件#inlcude<list>,并且使用std::命名空间

在这里插入图片描述

list是一个带头结点的双向循环链表

_head:指向其头结点

二. list_node类

1.构造函数

list_node(const T& val= T())
	:_data(val)
	,_prev(nullptr)
	,_next(nullptr)
{}

每一个结点,创建时_data = val,并将 _prev_next置空(nullptr)。

其中如果没有传val参数,则使用缺省值T():T类型的匿名对象(内置类型,如:int() = 0)

2.其他函数

其他成员函数使用编译器默认生成的函数就行,如:析构函数、拷贝构造、赋值运算符重载……

因为,如

  1. 拷贝构造:内置类型是按照字节方式直接拷贝的;自定义类型是调用其拷贝构造函数完成拷贝的
  2. 析构函数:无动态申请空间需要释放

三. 迭代器(iterator)

和string与vector的顺序存储不同,list存储是链式的。

所以list::iterator不能直接定义为元素指针,因为++、*(解引用)等对指针的操作在这里会出现问题

我们可以来封装原生指针(节点指针)成一个类,在类中完成运算符重载(operator 运算符),使++、*(解引用)等能完成其功能。此时iterator就变成像指针一样的对象了。

1.结构

template<class T, class Refence, class Pointer>
struct _list_iterator
{
    typedef list_node<T> Node;
	Node* _node;
};

2. 构造函数

_list_iterator(Node* node = nullptr)
	:_node(node)
{}

3. 运算符重载

//为书写方便,typedf
typedef _list_iterator<T, Refence, Pointer> iterator;
bool operator!=(const iterator& it)
{
    return _node != it._node;
}

bool operator==(const iterator& it)
{
    return _node == it._node;
}

Refence operator*()
{
    //返回节点的数据的引用
    return _node->_data;
}


Pointer operator->()
{ 
    //返回数据元素的指针
    return &(operator*());
}

// ++it
iterator& operator++()
{
    //自增1位
    _node = _node->_next;
    return *this;
}

// it++
//临时变量不能传引用
iterator operator++(int)
{
    //后置++,返回+1前的值
    iterator tmp(*this);
    _node = _node->_next;
    return tmp;
}

// --it
iterator& operator--()
{
    _node = _node->_prev;
    return *this;
}

// it--
//临时变量不能传引用
iterator operator--(int)
{
    iterator tmp(*this);
    _node = _node->_prev;
    return tmp;
}
operator->

在这里插入图片描述


->操作符:类对象指针访问其publibc成员
在这里插入图片描述

四.反向迭代器

反向迭代器与正向迭代器的区别主要是:移动方向不同。如:反向迭代器的++操作是向前走的

因此反向迭代器也需要重载运算符(operator 运算符),我们可以沿用正向迭代器的实现方法:将反向迭代器封装成一个类

同时我们可以使用正向迭代器来适配反向迭代器,即复用正向迭代器的代码

反向迭代器设计时,可以规定和正向迭代器相反
在这里插入图片描述

因此*rbegin的操作应该是先让rbegin向前移动1位再 *(解引用)

1.结构

template<class Iterator, class Refence, class Pointer>
struct _reverse_iterator
{
    Iterator _rit;
}

反向迭代器类的成员变量是一个正向迭代器的对象(适配)

2.构造函数

_reverse_iterator(const Iterator& it = Iterator())
		:_it(it)
	{}

3.运算符重载

//为书写方便,typedf
typedef _reverse_iterator<Iterator, Refence, Pointer> Self;
Refence operator*()
{
    Iterator tmp = _it;
    return *(--tmp);
}

Pointer operator->()
{
    return &(operator*());
}

Self& operator++()
{
    --_it;
    return *this;
}

Self& operator--()
{
    ++_it;
    return *this;
}

bool operator!=(const Self& s)
{
    return _it != s._it;
}

五. list常用方法及实现

1. 默认构造函数

a.empty_init

void empty_init()
{
    // 创建并初始化哨兵位头结点
    _head = new Node;
    _head->_next = _head;
    _head->_prev = _head;
}
list()
{
    empty_init();
}

2.迭代器

//正向迭代器
typedef _list_iterator<T, T&, T*> iterator;
typedef _list_iterator<T, const T&, const T*> const_iterator;
//反向迭代器
typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;
typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

反向迭代器第一个模板参数传入的正向迭代器的类 类型

a. begin

iterator begin()
{
    //返回迭代器对象
    return iterator(_head->_next);
}
const_iterator begin() const
{
    return const_iterator(_head->_next);
}

返回首元素的位置(_head的下一个元素)

b. end

iterator end()
{
    return iterator(_head);
}
const_iterator end() const
{
    return const_iterator(_head);
}

end()返回,尾元素的下一个位置(即 _head)

c.rbegin

reverse_iterator rbegin()
{
    return reverse_iterator(end());
}
const_reverse_iterator rbegin() const
{
    return const_reverse_iterator(cend());
}

d.rend

reverse_iterator rend()
{
    return reverse_iterator(begin());
}
const_reverse_iterator rend() const
{
    return const_reverse_iterator(cbegin());
}

3.数据访问

a. size

size_t size() const
{
	size_t count = 0;
	iterator cur = begin();
    
	while (cur != end())
	{
		++count;
	}
    
	return count;
}

遍历计数

b. empty

void empty() const
{
    return begin() == end();
}

c. front

T& front()
{
    return *begin();
}
const T& front() const
{
    return *begin();
}

返回首元素的引用

d. back

T& back()
{
    return *(--end());
}
const T& back() const
{
    return *(--end());
}

返回尾元素的引用

4.增删操作

a. insert

iterator insert(iterator pos, const T& val)
{
    //断言处理,避免pos指向一个空指针
    assert(pos._node);
	Node* cur = pos._node;

    Node* newnode = new Node(val);
    Node* prev = cur->_prev;
    Node* next = cur;
    
    //|prev| |newnode| |next|
    newnode->_prev = prev;
    prev->_next = newnode;
    newnode->_next = next;
    next->_prev = newnode;

    return iterator(newnode);
}

在pos前 插入一个值为val的结点

b. push_back

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

在尾元素的下一个位置前,插入值为val的结点

c. push_front

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

在首元素的位置前,插入值为val的结点

d. erase

iterator erase(iterator pos)
{
    assert(pos._node);//pos指向的非空指针
    assert(pos != end());//删除的不是尾元素的下一个位置
    
    Node* cur = pos._node;
    Node* prev = cur->_prev;
    Node* next = cur->_next;
    
 	//|prev| |cur| |next|
    prev->_next = next;
    next->_prev = prev;
    delete cur;

    return iterator(next);
}

删除pos指向的元素,并返回删除位置的下一个位置的迭代器

e. pop_front

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

删除首元素

f. pop_back

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

删除尾元素

g.clear

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

迭代释放所有节点空间(除头节点外)

5.初始化和清理

a. 析构函数

~list()
{
    clear();
    delete _head;
    _head = nullptr;
}

释放所有节点空间

b. n个val的构造

list(size_t n, const T& val = T())
{
    empty_init();

    for (size_t i = 0; i < n; ++i)
        push_back(val);
}

c. 迭代器区间构造

template <class InputIterator>  
list(InputIterator first, InputIterator last)
{
    empty_init();//创建头结点

    while (first != last)
    {
        push_back(*first);
        ++first;
    }
}

d. swap

void swap(list<T>& lt)
{
    std::swap(_head, lt._head);
}

e. 拷贝构造

传统写法:

list(const list<T>& lt)
{
    empty_init();//创建头结点
    
    for (const auto& e : lt)
    {
        push_back(e);
   	}
}

现代写法:

通过一个局部对象,和swap来完成

list(const list<T>& lt)
{
    empty_init();//创建头结点
    
    list<T> temp(lt.begin(), lt.end());
    swap(temp);
}

通过empty_init,对成员变量_head进行初始化,然后和经迭代器区间构造函数得到的temp对象进行交换内容,完成拷贝构造。该函数执行结束,局部对象temp会自动调用析构函数,并销毁。

f. operator=

传统写法:

list<T>& operator=(const list<T> lt)
{
    //如果是自己给自己赋值则不操作
    if (lt != this)
    {
        clear();
        for (const auto& e : lt)
        {
            push_back(e);
        }
    }
    return *this;
}

现代写法:

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

传参时,会调用拷贝构造函数完成对局部对象lt的初始化,然后交换*this和lt的内容,完成对自身对象的赋值。

六.小结

1.代码结构

可以将模拟实现的代码放在自己的命名空间中,避免冲突

#include <iostream>
#include <assert.h>

namespace yyjs
{
    template<class T>
	struct list_node
    {};
    
    template<class T, class Refence, class Pointer>
	struct _list_iterator
    {};
    
    template<class Iterator, class Ref, class Ptr>
	struct Reverse_iterator
    {};
    
    
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 Reverse_iterator<iterator, T&, T*> reverse_iterator;
		typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
          
    };
}

2.迭代器失效

插入元素时不会导致迭代器失效

iterator insert(iterator pos, const T& x);

在pos结点前插入一个元素,pos指向并未改变。

删除时迭代器会失效

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

对于上例中clear()函数中,每次进行删除操作后,it就是失效的迭代器(原先指向的空间被释放了),所以需要重新对it进行赋值(erase函数返回的是下一个位置的迭代器)。

3. 反向迭代器

由于list底层是一个双向链表,因此可以实现反向遍历等操作(–),所有可以实现其反向迭代器。

关于迭代器可以发现,对于客户(使用者)而言,不需要过多的了解使用的某个容器的底层实现原理,对于如string、vector与list,都提供了一个迭代器(iterator),我们甚至不需要考虑iterator具体是什么:是一个指针还是一个类?

在使用时,auto it = lti.begin(),然后就可以遍历容器的元素,并且他们的操作方法都相同,如it++、*it……


    🦀🦀观看~~

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

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

相关文章

8. WebGPU 平移变换

我们将开始编写与顶点缓冲区文章中的示例类似的代码&#xff0c;但这次将绘制单个 F 而不是一堆圆&#xff0c;并使用索引缓冲区来保持数据更小。 让我们在像素空间而不是裁剪空间中工作&#xff0c;就像 Canvas 2D API 我们将制作一个 F&#xff0c;将从 6 个三角形构建它 …

啥?PCB拼版对SMT组装有影响!

PCB为什么要拼版&#xff1f; 拼版主要是为了满足生产的需求&#xff0c;有些PCB板太小&#xff0c;不满足做夹具的要求&#xff0c;所以需要拼在一起进行生产。 拼版也可以提高SMT贴片的焊接效率&#xff0c;如只需要过一次SMT&#xff0c;即可完成多块PCB的焊接。 同时也可…

你知道ai绘画工具都有哪些吗?ai画图的软件分享给你

大家好&#xff01;你有没有想过&#xff0c;如果我们能有一种神奇的工具&#xff0c;可以帮助我们实现想象中的绘画作品&#xff0c;该有多好呢&#xff1f;现在&#xff0c;随着人工智能的发展&#xff0c;我们可以借助ai绘画工具来探索艺术的奇妙世界了&#xff01;不过你是…

又双叕搞事?我拿着这份“满级”的JVM笔记,拼进了阿里

JVM JVM&#xff0c;一个熟悉又陌生的名词&#xff0c;从认识Java的第一天起&#xff0c;我们就会听到这个名字&#xff0c;在参加工作的前一两年&#xff0c;面试的时候还会经常被问到JDK&#xff0c;JRE&#xff0c;JVM这三者的区别。 JVM&#xff08;Java Virtual Machine…

汽车IVI中控开发中视频相关的一些知识点

前言: 视频最早的渊源来源于电视。做汽车仪表/IVI中控,尤其是IVI信息娱乐部分,都要涉及到视频这个知识点,各种概念很多,首先需要明确一条主线,那就是SDTV标清电视->HDTV高清电视->UHDTV超高清电视的一个发展脉络,BT601/656是SDTV标清电视接口,BT1120则对应HDTV高…

ShardingSphere

一、基本概念 1、什么是ShardingSphere 1、一套开源的分布式数据库中间件解决方案 2、有三个产品&#xff1a;Sharding-JDBC和Sharding-Proxy、Sharding-Sidecar 3、定位为关系型数据库中间件&#xff0c;合理在分布式环境下使用关系型数据库操作 2、分库分表 1什么是分库…

文物和古建筑防雷综合解决方案

文物和古建筑作为珍贵的历史遗产&#xff0c;需要受到专业的防雷保护&#xff0c;以保持其完整性和安全性。本文将介绍详细的文物和古建筑防雷方案和措施&#xff0c;包括避雷针安装、接地系统建设、监测技术和定期维护等。 引言&#xff1a; 文物和古建筑承载着珍贵的历史记忆…

防雷抗浪涌插排插座推荐,同为科技(TOWE)防雷桌面PDU安全可靠

同为科技TOWE双排防雷抗浪涌桌面PDU插座 随着夏天天气越来越热&#xff0c;强对流天气增多&#xff0c;雷雨天气频发。在雷电季节&#xff0c;通常影响家用电器安全的主要原因是由于雷电感应的侵入&#xff0c;特别是对绝缘强度低、过电压耐受力差的微电子产品影响甚大。而所谓…

JVM笔记(二)

JVM内存管理 在之前&#xff0c;我们了解了JVM的大致运作原理以及相关特性&#xff0c;这一章&#xff0c;我们首先会从内存管理说起。 在传统的C/C开发中&#xff0c;我们经常通过使用申请内存的方式来创建对象或是存放某些数据&#xff0c;但是这样也带来了一些额外的问题&…

一次XxlJob调度任务重复执行的问题排查

目录 东老师的问题1. 为什么会重复执行2. 为什么时间间隔改为1min就不会重复执行** 开始排查先看下任务配置 任务第一次执行排查执行类 》JobThreadJobThread的核心逻辑1.循环消费 一个阻塞队列 不断的去消费队列中TriggerParam 这个参数2.看下TriggerParam&#xff0c;这正是我…

硬件学习 软件 Cadence day10 查看网表导入进度,钻孔保护等一些操作

1. 查看网表导入状态。 2. 放置元器件 之前 画板框 3.放置元器件 4.把导入的DXF 文件变成板框 1.首先导入DXF文件 2. 点击按钮 3. 鼠标点击需要 调整为板框的地方 3.1 其中包括边框 3.2 固定的钻孔 5.给钻孔打上保护&#xff08;防止布线的时候区域错误&#xff0c;在固定的时…

12. AbstractQueuedSynchronizer之AQS

12.1 前置知识 ● 公平锁和非公平锁 ○ 公平锁&#xff1a;锁被释放以后&#xff0c;先申请的线程先得到锁。性能较差一些&#xff0c;因为公平锁为了保证时间上的绝对顺序&#xff0c;上下文切换更频繁 ○ 非公平锁&#xff1a;锁被释放以后&#xff0c;后申请的线程可能会先获…

第58讲:Python编程中最难以理解的递归函数核心概念以及应用案例

文章目录 1.递归函数的概念2.递归函数的使用2.1.案例一2.1.1.需求描述2.1.2.使用常规的循环来实现2.1.3.使用递归函数实现 2.2.案例二2.2.1.需求描述2.2.2.使用常规的循环来实现2.2.3.使用递归函数实现 3.使用递归函数计算阶乘3.1.阶乘的概念3.2.使用递归函数实现阶乘的算法3.3…

分布式Profinet IO模块

PROFINET IO模块是一种用于工业自动化控制系统中的设备控制模块。它使用以太网技术&#xff0c;在现场设备和处理器/控制器之间提供快速、精确和可靠的数据交换。PROFINET IO模块通常是面向过程的&#xff0c;可以用于监测和控制工业过程中的各种设备和参数&#xff0c;如传感器…

Vue中使用editor.md(2):添加拖拽图片上传功能

0. 背景 在对editor.md简单使用后&#xff0c;希望添加图片拖拽或粘贴上传的功能。 简单使用参考&#xff1a;Vue中使用editor.md&#xff08;1&#xff09;&#xff1a;简单使用 1. 实现 1.1 添加粘贴监听 // 使用axios import axios from /api/indexfunction initPasteDra…

什么是Vue的插件?如编写自定义 Plugin?

什么是Vue的插件&#xff1f; 在Vue开发中&#xff0c;我们经常需要使用一些第三方库或功能性模块&#xff0c;Vue插件就是一种将这些库或模块集成到Vue应用中的方式。插件是Vue.js提供的一种机制&#xff0c;用于扩展Vue的功能。插件通常用于封装某些特定的功能&#xff0c;例…

【AI人工智能】 你如果要使用最强大的语言模型,你还要有最精美的浏览器标签页iTab (2)

&#x1f680; 个人主页 极客小俊 ✍&#x1f3fb; 作者简介&#xff1a;web开发者、设计师、技术分享博主 &#x1f40b; 希望大家多多支持一下, 我们一起进步&#xff01;&#x1f604; &#x1f3c5; 如果文章对你有帮助的话&#xff0c;欢迎评论 &#x1f4ac;点赞&#x1…

UnityA*导航算法,通俗易懂

首先A*寻路算法分为2D算法和3D算法&#xff0c;目前它甚至不如NAVMesh算法效率高。但NAVMesh不适用于2D&#xff0c;因此2D还是要靠A*算法来进行实现。 当前就来说说2D是如何实现的。 首先2DA*算法先要将地图划分成格子分块标记成二维数组 每个格子依据x&#xff0c;y&#xf…

又一新型技术全面铺开,高精度光刻机已突破壁垒,赶超只是时间

众所周知&#xff0c;光刻机是制造高精度芯片的关键步骤。 随着科技的不断进步&#xff0c;光刻机的精度和速度也在不断提高&#xff0c;使得我们可以制造出更小更精细的芯片&#xff0c;满足了人们对于高性能电子设备的需求。 我国的光刻机技术&#xff0c;国产大飞机&#x…

【华为OD机试真题2023B卷 JAVAJS】二维伞的雨滴效应

华为OD2023(B卷)机试题库全覆盖,刷题指南点这里 二维伞的雨滴效应 知识点递归树 时间限制:1s 空间限制:256MB 限定语言:不限 题目描述: 普通的伞在二维平面世界中,左右两侧均有一条边,而两侧伞边最下面各有一个伞坠子,雨滴落到伞面,逐步流到伞坠处,会将伞坠的信息携…