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

8.list的模拟实现

一.源码分析

1.成员变量

链表里面存储的就是节点的指针

2.构造函数

我们就可以知道,list是双向链表

3.push_back()函数分析

在position位置之前插入节点

二.定义成员函数

namespace ltw
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;};template<class T>class List{typedef ListNode<T> Node;public:List(){head = new Node;_head->_next = _head;_head->_prev = _head;}private:Node* _head;};
}

三.实现push_back()函数

void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}

我们在使用模板的时候,在编译的时候是不会细节检查的,只有实例化(不实例化也不会报错),才会去检查

按需实例化(不调用就不实例化这个成员函数)

所以我们要给push_back()提供一个ListNode(const T& data)的构造函数

template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data):_next(nullptr),_prev(nullptr),_data(data){}};

我们这里使用_next和_prev很频繁,所以我们对于整个类使用struct(全部用公有)

List(){head = new Node(T());_head->_next = _head;_head->_prev = _head;}

所以我们还不如直接将上面的写成全缺省的类型,代码如下:

namespace ltw
{template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data){}};template<class T>class List{typedef ListNode<T> Node;public:List(){head = new Node;_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}private:Node* _head;};
}

四.遍历list(重点)

1.迭代器的实现

我们不能使用前两个数据结构的方法 typedef Node* iterator (因为++没办法取到我们的下一个节点的)

我们重新设计一个类,来实现迭代器的功能

template<class T>class ListIterator{typedef ListNode<T> Node;Node* _node;};

template<class T>class ListIterator{typedef ListNode<T> Node;typedef ListIterator<T> Self;Node* _node;ListIterator(Node* node):_node(node){}// ++itSelf& operator++(){_node = _node->_next;return *this;}T& operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}};

迭代器类的主要实现就是, ++ , * , != 这三个运算符,我们就先写这三个

2.在List里面引入迭代器

1.begin()的实现

typedef ListIterator<T> iterator;iterator begin(){iterator it(_head->_next);return it;}

2.end()的实现

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

end()是最后一个元素的下一个位置,所以是我们的哨兵位

3.List类的总代码

template<class T>class List{typedef ListNode<T> Node;public:typedef ListIterator<T> iterator;iterator begin(){// iterator it(_head->_next);// return it;return iterator(_head->_next);}iterator end(){return iterator(_head);}List(){head = new Node;_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}private:Node* _head;};

测试代码void test_list1(){list<int> lt1;// 按需实例化(不调用就不实例化这个成员函数)lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);// Func(lt1);//ListIterator<int> it = lt1.begin();list<int>::iterator it = lt1.begin();while (it != lt1.end()){*it += 10;cout << *it << " ";++it;}cout << endl;for (auto e : lt1){cout << e << " ";}cout << endl;}

内置类型不能改变运算符的规则,但是我要是把内置类型进行封装,然后对封装的类进行重载,这样就间接的将我们的内置类型进行重载了

以后,对于红黑树,哈希表,我们都可以是要上述的方式进行操作,达到我们想要的效果

4.完善我们迭代器的运算符

1.重载++,--(前置和后置)

// ++itSelf& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}

2.operator==()

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

operator+效率太低了,我们就不重载了(库里面也没重载)

迭代器的节点是不用进行析构的,因为我们的节点不是单独开的,而是在链表里面的,(析构交给链表)

不要越级管理

3.拷贝构造(这个地方就是浅拷贝)

我们要拷贝的话,就是希望把我们的指针拷贝给你,默认的拷贝构造就够用

4.重载operator->

T* operator->(){return &_node->_data;}

理解为啥要  重载operator->运算符?

我们要是想用,就能够重载 operator <<,但是我们也可以进行重载operator->

下面这种方式也能进行访问,但是不够方便

(*it) 表示的就是pos,pos用来访问struct内的x,y使用  .     

(it)  表示pos的地址,地址访问里面的数据使用  ->

struct Pos{int _row;int _col;Pos(int row = 0, int col = 0):_row(row),_col(col){}};void test_list2(){list<Pos> lt1;lt1.push_back(Pos(100, 100));lt1.push_back(Pos(200, 200));lt1.push_back(Pos(300, 300));list<Pos>::iterator it = lt1.begin();while (it != lt1.end()){//cout << (*it)._row << ":" << (*it)._col << endl;// 为了可读性,省略了一个->cout << it->_row << ":" << it->_col << endl;//cout << it->->_row << ":" << it->->_col << endl;cout << it.operator->()->_row << ":" << it.operator->()->_col << endl;++it;}cout << endl;}

5.编译器的优化的解释:

具体的解释如下:

6.const变量的常性和临时变量的常性的区别

相当于是位于const和非const的一个中间态

7.const迭代器的生成

const迭代器,本身是可以修改的,但是指向的内容不能进行修改

因为const迭代器是值不能修改而指向能进行修改

template<class T>class ListConstIterator{typedef ListNode<T> Node;typedef ListConstIterator<T> Self;Node* _node;public:ListConstIterator(Node* node):_node(node){}// ++it;Self& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}const T& operator*(){return _node->_data;}//const T* it  -->  修饰的是T,如int不能被修改,但是指向能修改(我们的迭代器还是能修改的)const T* operator->(){return &_node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};

然后把他们const的类型的加入到List类里面

typedef ListConstIterator<T> const_iterator;const_iterator begin() const{// iterator it(_head->_next);// return it;return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}

然后我们发现const_iterator类和iterator类只有返回类型不同

我们可以通过控制模板参数来进行实现代码的复用

代码如下:

template<class T>struct ListNode{ListNode<T>* _next;ListNode<T>* _prev;T _data;ListNode(const T& data = T()):_next(nullptr),_prev(nullptr),_data(data){}};template<class T,class Ref,class Ptr>struct ListIterator{typedef ListNode<T> Node;typedef ListIterator<T,Ref,Ptr> Self;Node* _node;ListIterator(Node* node):_node(node){}// ++itSelf& operator++(){_node = _node->_next;return *this;}Self& operator--(){_node = _node->_prev;return *this;}Self& operator++(int){Self tmp(*this);_node = _node->_next;return tmp;}Self& operator--(int){Self tmp(*this);_node = _node->_prev;return tmp;}Ptr operator->(){return &_node->_data;}Ref operator*(){return _node->_data;}bool operator!=(const Self& it){return _node != it._node;}bool operator==(const Self& it){return _node == it._node;}};template<class T>class List{typedef ListNode<T> Node;public:// typedef ListIterator<T> iterator;// typedef ListConstIterator<T> const_iterator;typedef ListIterator<T,T&,T*> iterator;typedef ListIterator<T,const T&,const T*> const_iterator;const_iterator begin() const{// iterator it(_head->_next);// return it;return const_iterator(_head->_next);}const_iterator end() const{return const_iterator(_head);}iterator begin(){// iterator it(_head->_next);// return it;return iterator(_head->_next);}iterator end(){return iterator(_head);}List(){head = new Node;_head->_next = _head;_head->_prev = _head;}void push_back(const T& x){Node* newnode = new Node(x);Node* tail = _head->_prev;tail->_next = newnode;newnode->_prev = tail;newnode->_next = _head;_head->_prev = newnode;}private:Node* _head;};

五.insert(),erase(),pop_back()和pop_front()的实现

1.insert()实现:

void insert(iterator pos,const T& x){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = newnode;newnode->_prev = prev;newnode->_next = cur;cur->_prev = newnode;}

链表的迭代器,不会由迭代器失效,因为没有扩容的概念,但是库里面有返回值我们还是把返回值带上

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

2.erase()实现:

//erase后 pos失效,因为当前节点被删除了void erase(iterator pos){Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;}

erase有迭代器失效,我们要返回删除元素的后面一个元素

//erase后 pos失效,因为当前节点被删除了iterator erase(iterator pos){assert(pos != end());Node* cur = pos._node;Node* newnode = new Node(x);Node* prev = cur->_prev;prev->_next = next;next->_prev = prev;delete cur;return iterator(next);}

3.pop_back()实现:

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

我们可以直接复用

4.pop_front()实现:

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

5.push_front()的实现

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

测试代码:

void test_list4(){list<int> lt1;lt1.push_back(1);lt1.push_back(2);lt1.push_back(3);lt1.push_back(4);lt1.push_back(5);Func(lt1);lt1.push_front(10);lt1.push_front(20);lt1.push_front(30);Func(lt1);lt1.pop_front();lt1.pop_front();Func(lt1);lt1.pop_back();lt1.pop_back();Func(lt1);lt1.pop_back();lt1.pop_back();lt1.pop_back();lt1.pop_back();//lt1.pop_back();Func(lt1);}

六.list的拷贝

我们的迭代器想要的是浅拷贝,但是我们的list不能浅拷贝啊

1.析构函数的补充(浅拷贝会析构两次)

一般我们链表都是会实现clear()的,所以我们先实现clear(),然后我们在写析构函数的时候,进行复用

1.clear()

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

2.~list()

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

3.重新默认构造

void empty_init(){head = new Node;_head->_next = _head;_head->_prev = _head;}List(){empty_init();}

将哨兵位的头节点单拎出来,变成一个head

4.拷贝构造

//lt2(lt1)list(const list<T>& lt){empty_init();for(auto e : lt){push_back(e);}}

我们这里最好加上 const 和 & ,避免拷贝(这里的引用,是引用的lt里面的值)

//lt2(lt1)list(const list<T>& lt){empty_init();for(const auto& e : lt){push_back(e);}}

2.operator=的重载

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

3.initializer_list的构造

//这里是两个指针,所以我们可以不传&list(initializer_list<T> il){empty_init();for(const auto& e:il){push_back(e);}}

vs的这种写法会更好,但是我们还是要代码写得规范

http://www.dtcms.com/a/490453.html

相关文章:

  • 鸿蒙NEXT按键拦截与监听开发指南
  • 网站建设等级定级企查查官网查企业网页版
  • 【数据结构】基于Floyd算法的最短路径求解
  • 【传感器技术】入门红外传感器技术
  • 成都哪里做网站便宜郴州新网招聘官网
  • 天地一体:卫星互联网与5G/6G的融合之路
  • BCH码编译码仿真与误码率性能分析
  • 5G+AIoT智赋,AI电力加密边缘网关智慧电网数字化运维解决方案
  • 深度学习:PyTorch Lightning,训练流程标准化?
  • 100G 单纤光模块:高带宽传输新选择,选型与应用全解析
  • 网站开发的技术有gis网站开发实战教程
  • 汕头网站建设技术外包模板网站怎么用
  • 2025-10-16-TH 开源框架JeecgBoot Pro搭建流程
  • 二叉树搜索树插入,查找,删除,Key/Value二叉搜索树场景应用+源码实现
  • 2025年10月版集成RagFlow和Dify的医疗知识库自动化查询(数据篇)
  • UVa 12803 Arithmetic Expressions
  • json转excel xlsx文件
  • 【C++】深入理解string类(5)
  • 六、Hive的基本使用
  • 铜陵网站建设推广江苏核酸检测机构
  • 电子商务网站建设含义如果做车站车次查询的网站需要什么消息信息
  • 【JETSON+FPGA+GMSL】实测分享 | 如何实现激光雷达与摄像头高精度时间同步?
  • 建网站权威公司dw怎么做打开网站跳出提示
  • 阅读:REACT: SYNERGIZING REASONING AND ACTING INLANGUAGE MODELS(在语言模型中协同推理与行动)
  • 语义三角论对AI自然语言处理中深层语义分析的影响与启示
  • SpringBoot 启动时执行某些操作的 8 种方式
  • Cloud IDE vs 本地IDE:AI编程时代的“降维打击“
  • RocketMQ 事务消息
  • 做网站的不肯给ftp企业163邮箱登录
  • reactNative 遇到的问题记录