当前位置: 首页 > news >正文

STL详解——list的模拟实现

目录

1.list的定义

2.迭代器

2.1基本功能实现

2.2const迭代器

2.2.1基本迭代器的完善

2.2.2const迭代器的基本实现

2.2.3模板优化语法

2.3自定义类型数据迭代器

2.3.1使用情景

2.3.2嵌套类模板

3.其他接口的实现

3.1insert与erase

3.2头插与头删

3.3析构与拷贝构造

3.4赋值运算符重载与构造扩展


1.list的定义

STL中的list容器不同于数据结构中常见的单链表,而是一种带头双向链表,因此在实现过程中也以此结构为基础实现。对于链表,最基本的便的其结点,因此我们先实现结点的定义:

template<class T>
struct _list_node
{T val;_list_node* next;_list_node* prev;
};

 完成结点的定义后,我们便成将list容器抽象为以头结点(哨兵位)为头的多结点链,结点间能通过彼此互相访问。因此list的成员变量便是头结点(_head)。

对list进行初始化,因是带头双向链表,因此初始化就是将头结点初始化为一个next与prev都指向头的结点,但在这之前需要给_head申请(new)一个结点(Node)的空间,new自定义类型的时候会调用结点类的构造函数,因此这时还需要完善结点类的构造函数

//结点
template<class T>
struct _list_node
{T val;_list_node* next;_list_node* prev;//完善构造_list_node():val(0),next(nullptr),prev(nullptr){}
};//list链表
template<class T>
class list
{
public:typedef _list_node<T> Node;//构造list(){_head = new Node;_head->next = _head;_head->prev = _head;}private:Node* _head;
};

2.迭代器

2.1基本功能实现

迭代器的基本功能就是通过类指针的形式来遍历容器元素,对于vector这种底层为数据的容器,可以用最简单的指针来完成迭代器的封装,而list这种底层非连续空间的容器,则需要将遍历的方式封装为一种迭代器类。初步接触迭代器封装,可以通过使用来逐步实现:

namespace my_list
{void test01(){list<int> lt1;list<int>::iterator it = lt1.begin();while (it != lt1.end()){cout << *it << ' ';++it;}cout << endl;}
}

迭代器最基本的使用无非就是要完成上面的功能,list最基本元素是结点,因此迭代器成员变量代表的也应是结点, 接下来要实现的便是++迭代器能完成指向下一个结点,迭代器解引用能返回结点中的值。代码如下:

	//迭代器(一、初代)template<class T>struct _list_iterator{typedef _list_node<T> Node;Node* _node;_list_iterator(Node* node):_node(node){}T& operator*(){return _node->val;}_list_iterator<T> operator++(){_node = _node->next;return *this;}bool operator!=(const _list_iterator<T>& it)//注意不能使用非const引用{return _node != it._node;}};//list链表template<class T>class list{public:typedef _list_node<T> Node;typedef _list_iterator<T> iterator;//构造list(){_head = new Node;_head->next = _head;_head->prev = _head;}//迭代器iterator begin(){return iterator(_head->next);}iterator end(){return iterator(_head);}private:Node* _head;};

这里需要注意的是begin与end的返回值为临时对象(具有常性)不能用非const引用来接受,因此在!=重载函数中的参数选择为const引用。

下面通过push_back插入数据来检验一下:

void push_back(const T& x)
{Node* newnode = new Node;newnode->val = x;newnode->next = _head;newnode->prev = _head->prev;_head->prev->next = newnode;_head->prev = newnode;
}
namespace my_list
{void test01(){list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);list<int>::iterator it = lt1.begin();while (it != lt1.end()){cout << *it << ' ';++it;}cout << endl;}
}int main()
{my_list::test01();return 0;
}

运行结果如下:

这里也算基本完成迭代器功能。

2.2const迭代器

2.2.1基本迭代器的完善

在完成const迭代器前,先对之前的基本迭代器进行完善,即完善相对应的运算符重载。

_list_iterator<T> operator++(int)
{_list_iterator<T> tmp(*this);_node = _node->next;return *this;
}_list_iterator<T>& operator--()
{_node = _node->prev;return *this;
}_list_iterator<T> operator--(int)
{_list_iterator<T> tmp(*this);_node = _node->prev;return tmp;
}bool operator== (const _list_iterator<T>& it)
{return _node == it._node;
}

2.2.2const迭代器的基本实现

const迭代器的功能是迭代器可以修改,但让迭代器指向的内容不能修改。因此这里的迭代器不能像vector中的直接在iterator前加const修饰,因为vector中迭代器底层是指针,const修饰的实际是指针类型;而list中迭代器是一种自定义类型,使用const修饰的效果为迭代器不能修改,但迭代器指向的内容可以修改。

因此这里要实现const迭代器功能主要是让解引用返回的对象不能修改,也就是让返回值变为const T&即可。

但这里的iterator需变为const_iterator,类的类型发生了改变,因此这里需要再重新创建一个const迭代器类:

//二、const迭代器
template<class T>
struct _list_iterator
{typedef _list_node<T> Node;Node* _node;_list_iterator(Node* node):_node(node){}T& operator*(){return _node->val;}_list_iterator<T> operator++(){_node = _node->next;return *this;}bool operator!=(const _list_iterator<T>& it){return _node != it._node;}_list_iterator<T> operator++(int){_list_iterator<T> tmp(*this);_node = _node->next;return *this;}_list_iterator<T>& operator--(){_node = _node->prev;return *this;}_list_iterator<T> operator--(int){_list_iterator<T> tmp(*this);_node = _node->prev;return tmp;}bool operator== (const _list_iterator<T>& it){return _node == it._node;}
};template<class T>
struct _list_const_iterator
{typedef _list_node<T> Node;Node* _node;_list_const_iterator(Node* node):_node(node){}_list_const_iterator(const _list_iterator<T>& it):_node(it._node){ }const T& operator*(){return _node->val;}_list_const_iterator<T> operator++(){_node = _node->next;return *this;}bool operator!=(const _list_const_iterator<T>& it){return _node != it._node;}_list_const_iterator<T> operator++(int){_list_const_iterator<T> tmp(*this);_node = _node->next;return *this;}_list_const_iterator<T>& operator--(){_node = _node->prev;return *this;}_list_const_iterator<T> operator--(int){_list_const_iterator<T> tmp(*this);_node = _node->prev;return tmp;}bool operator== (const _list_const_iterator<T>& it){return _node == it._node;}
};

list中迭代器相关函数const重载如下:

//迭代器
iterator begin()
{return iterator(_head->next);
}iterator end()
{return iterator(_head);
}const_iterator begin()const
{return const_iterator(_head->next);
}const_iterator end()const
{return const_iterator(_head);
}

在const迭代器类中为支持非const迭代器转换为const迭代器,重载了使用迭代器初始化的拷贝构造。

这里能不能将代码进行简化呢?

2.2.3模板优化语法

const迭代器仅仅是在解引用时返回值不同,按前面写法便要写两个类。若这时将解引用返回的类型用模板表示,这时通过不同的模板实例化也能区分两种类。具体实现方法如下:

//二、const迭代器
template<class T, class Ref>
struct _list_iterator
{typedef _list_node<T> Node;typedef _list_iterator<T, Ref> Self;Node* _node;_list_iterator(Node* node):_node(node){}_list_iterator(const _list_iterator<T, T&>& it):_node(it._node){}Ref operator*(){return _node->val;}Self operator++(){_node = _node->next;return *this;}bool operator!=(const Self& it){return _node != it._node;}Self operator++(int){Self tmp(*this);_node = _node->next;return *this;}Self& operator--(){_node = _node->prev;return *this;}Self operator--(int){Self tmp(*this);_node = _node->prev;return tmp;}bool operator== (const Self& it){return _node == it._node;}
};

这样通过模板实例化的不同也能实现const迭代器与普通迭代器的分离。

2.3自定义类型数据迭代器

2.3.1使用情景

list作为一种顺序容器,除储存内置类型外,也可储存自定义类型,如储存位置数据(Pos):

struct Pos
{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}
};void test02()
{list<Pos> lt1;list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){}
}

对于这种自定义类型,重载的解引用出来是Pos结构体,要访问其中数据还要自己处理:

void test02()
{list<Pos> lt1;lt1.push_back({ 1,1 });lt1.push_back({ 2,2 });lt1.push_back({ 3,3 });list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){cout << (*it)._row << ':' << (*it)._col << endl;++it;}
}

但在STL库中,迭代器对于这种自定义类型还提供了类似结构体指针的访问方法:

但对于我们自己的list容器,迭代器并不是指向该自定义类型指针,因此这里实际是对->运算符的重载。->的重载实际是要返回Pos的指针,这里为了区分const与非const,依旧使用模板优化语法。 

template<class T, class Ref, class Ptr>
struct _list_iterator
{typedef _list_node<T> Node;typedef _list_iterator<T, Ref, Ptr> Self;Ptr operator->(){return &(_node->val);}
};

但这里需要注意:->重载后返回的是结构体的指针,即整个it->代表的是结构体指针,原则上需要在it->后再使用->调用数据,但编译器简化成了一个->。

2.3.2嵌套类模板

先来看看下面场景:

template <class T>
void Print(const list<T>& lt)
{list<T>::const_iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';++it;}cout << endl;
}

运行结果如下:

可以看到这种写法是错误的,原因是在类模板未实例化时,不能去找类模板后面东西,编译器分不清const_iterator是嵌套内类,还是静态成员变量。这时可以用typename告诉编译器这里是类型:

template <class T>
void Print(const list<T>& lt)
{typename list<T>::const_iterator it = lt.begin();while (it != lt.end()){cout << *it << ' ';}cout << endl;
}

当然也可以用auto接收迭代器:

auto it = lt.begin();

3.其他接口的实现

在实现完迭代器后,便能接着实现许多依托于迭代器功能的接口。

3.1insert与erase

insert与erase接口实现逻辑类似:

iterator insert(const iterator& pos, const T& x)
{Node* newnode = new Node(x);Node* cur = pos._node;newnode->prev = cur->prev;newnode->next = cur;cur->prev->next = newnode;cur->prev = newnode;return iterator(newnode);
}iterator erase(const iterator& pos)
{Node* next = pos._node->next;Node* prev = pos._node->prev;next->prev = prev;prev->next = next;delete pos._node;return iterator(next);
}

3.2头插与头删

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

3.3析构与拷贝构造

void empty_init()
{_head = new Node;_head->next = _head;_head->prev = _head;
}//构造
list()
{empty_init();
}list(const list<T>& lt)
{empty_init();const_iterator it = lt.begin();while (it != lt.end()){push_back(*it);++it;}
}~list()
{clear();delete _head;_head = nullptr;
}void clear()
{iterator it = begin();while (it != end()){it = erase(it);}
}

3.4赋值运算符重载与构造扩展

list(std::initializer_list<T> il)
{empty_init();for (const auto& e : il){push_back(e);}
}//赋值重载
void swap(list<T>& lt)
{if (this != &lt){std::swap(_head, lt._head);}
}list<T>& operator=(list<T> lt)
{swap(lt);return *this;
}

http://www.xdnf.cn/news/933463.html

相关文章:

  • 【CSS-7】深入解析CSS伪类:从基础到高级应用
  • 【Linux】gcc、g++编译器
  • 香橙派3B学习笔记8:snap安装管理软件包_打包俩个有调用的python文件
  • 机器人/智能车纯视觉巡线经典策略—滑动窗口+直方图法
  • Unity3D 开发中的创新技术:解锁 3D 开发的新境界
  • SQL 注入开放与修复
  • NLP学习路线图(三十三): 文本分类
  • LiveCycle Designer 创建提交表单
  • FlexRay总线
  • web架构4------(nginx常用变量,nginx中英文自动匹配,lnmp网站架构,正向代理,反向代理,负载均衡)
  • GPU虚拟化
  • 【 SpringCloud | 微服务 MQ基础 】
  • 【AS32系列MCU调试教程】深度解析:使用 Eclipse 调试AS32系列MCU芯片的工程搭建
  • 永磁同步电机无速度算法--自适应龙贝格观测器
  • 技术栈Etcd的介绍和使用
  • RMQ 算法详解(区间最值问题)
  • 自然语言处理——文本分类
  • Unity使用代码分析Roslyn Analyzers
  • 湖北理元理律师事务所视角:企业债务优化的三维平衡之道
  • Python训练打卡Day43
  • 十二.理解Const关键字
  • JS Day04
  • Polarctf2025夏季赛 web java ez_check
  • 进程优先级
  • ffmpeg(五):裁剪与合并命令
  • 二叉树“倒着看”:层次遍历的反向打开方式
  • 分库分表的取舍
  • 禅道18.2集成LDAP
  • mac:大模型系列测试
  • 原型对象(Prototype)详解