STL之list

news/2025/1/15 17:48:31/

目录

  • 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.ppmy.cn/news/361665.html

相关文章

阿里P8传授的80K+星的MySQL笔记助我修行,一周快速进阶

MySQL 是最流行的关系型数据库之一&#xff0c;广泛的应用在各个领域。下面这些问题对于程序员的你来说应该很常见&#xff0c;来看看你面对这些问题是否会胆怯? MySQL数据库作发布系统的存储&#xff0c;一天五万条以上的增量&#xff0c;预计运维三年,怎么优化&#xff1f; …

QT--Opencv图片处理

提示&#xff1a;本文为学习记录&#xff0c;若有疑问&#xff0c;请联系作者。 文章目录 前言一、读取照片二、保存照片三、图像算法1.高斯滤波2.均值滤波3.中值滤波4.双边滤波5.其他操作 四、插值算法五、Mat和QImage互转1.Mat转QImage2.QImage转Mat 六、总结 前言 忙碌里寻…

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

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

神奇宝贝HTML游戏代码,口袋妖怪魂银金手指代码

口袋妖怪魂银金手指代码是一款全新口袋妖怪推出的RPG动作手游&#xff0c;全新的捕获体验&#xff0c;同是在这里大家可以更加完善的完成每一次的精灵捕获体验&#xff0c;玩法非常的轻松简单&#xff0c;大量不同的任务等待着你来解锁挑战&#xff0c;喜欢的小伙伴们快来下载吧…

生活有时候还是需要点这个的

1、乌龟正在河里洗澡被癞蛤蟆看见了&#xff0c; 乌龟&#xff1a;没见过像我这样的美女吗&#xff1f;看你眼珠子都快要蹦出来了。 癞蛤蟆&#xff1a;妹&#xff0c;你就别逗我了&#xff0c;没有看见我身上已经起鸡皮疙瘩了吗&#xff1f; 2、黄莺看到在寻食的黄鼠狼说&…

【程序猿的小幽默】

在论坛里看到的&#xff0c;感觉很有意思&#xff0c;所以转载过来了&#xff0c;不知道未经许可这样做好不好&#xff0c;附上原地址&#xff1a; http://bbs.csdn.net/topics/390767610 1.老婆给当程序员的老公打电话&#xff1a;下班顺路买十个包子&#xff0c;如果看到卖西…

[六点]Pygame零基础入门:极简开发框架

[六点]Pygame零基础入门&#xff1a;极简开发框架 参考 Pygame官方文档 嵩天教授的Python游戏开发教程 前言 在入门游戏开发时&#xff0c;pygame框架可以快速协助开发小型游戏。轻松而愉快的开始是从玩游戏到做游戏的关键&#xff0c;游戏的设计思想是游戏娱乐性的核心&am…

[转][darkbaby]任天堂传——失落的泰坦王朝(下)

即使是日本业界人士也对1999年发生的“口袋妖怪所有权风波”知之甚少&#xff0c;实际上这个事件的结局足以改变游戏产业未来数十年的势力图&#xff0c;山内溥凭借着个人的睿智让任天堂再次渡过了命运的暗礁&#xff0c;而另一颗曾经炙手可热的璀璨明星却从此销声匿迹…… 株式…