目录

一.框架

二.list迭代器

1.list迭代器的特殊之处

2.iterator类的代码实现

3.const迭代器复用普通迭代器

三.反向迭代器

1.反向迭代器也称迭代器适配器(复用的思想)

2.反向迭代器的使用

3.反向迭代器操作原理详解

4.反向迭代器的代码实现

5.迭代器的分类

四.常用接口

1.构造函数

2.insert与erase

3.其余接口


一.框架

list: 带头双向循环链表

//节点类
template<class T>
struct __list_node
{T _val;__list_node* _next;__list_node* _prev;__list_node(const T& x = T()):_val(x), _next(nullptr), _prev(nullptr){}
};//iterator类
template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_node<T> node;typedef __list_iterator<T, Ref, Ptr> iterator;typedef Reverse_iterator<iterator, T&, T*> reverse_iterator;typedef Reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;    //属性node* _node;__list_iterator(node* x);iterator operator++();iterator operator++(int);iterator operator--();iterator operator--(int);Ref operator*();Ptr operator->();bool operator!=(iterator it);    bool operator==(iterator it);
};
//list类
template<class T>
class list
{
public://正向迭代器typedef __list_node<T> node;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;iterator begin();iterator end();const_iterator begin() const;const_iterator end() const;///反向迭代器reverse_iterator rbegin();reverse_iterator rend();const_reverse_iterator rbegin() const;const_reverse_iterator rend() constvoid InitializeList();//构造list();list(int n, const T& value = T());template <class Iterator>list(Iterator first, Iterator last);list(const list<T>& l);list<T>& operator=(const list<T> l);//其余接口void swap(list<T>& l);bool empty();void push_back(const T& x);void pop_back();void push_front(const T& val);void pop_front();T& front();const T& front()const;T& back();const T& back()const;size_t size() const;iterator insert(iterator pos, const T& val);iterator erase(iterator pos);void clear();~list();
private:node* _head;
};

二.list迭代器

1.list迭代器的特殊之处

在string和vector中, 迭代器是原生指针T*, 具体形式: typedef T* iterator

但在list中, 节点的存储是通过指针链接起来的, 底层内存存储空间并不连续, 无法对于节点的指针进行++或者--一系列的操作, 解决这一问题, 是实现list迭代器的关键

我们可以使用操作符重载将++重载成类似于node* cur, cur = cur->next, 针对于iterator的一整套操作符方法我们全部进行重载, 所以, 我们创建了一个iterator类模板, 每次通过调用list类中begin()/end()的方式构造这个类对象it

2.iterator类的代码实现

template<class T, class Ref, class Ptr>
struct __list_iterator
{typedef __list_node<T> node;typedef __list_iterator<T, Ref, Ptr> iterator;node* _node;__list_iterator(node* x){_node = x;}//前置++iterator& operator++(){_node = _node->_next;return *this;}//后置++iterator operator++(int){iterator tmp = *this;_node = _node->_next;return tmp;}//前置--iterator& operator--(){_node = _node->_prev;return *this;}//后置--iterator operator--(int){iterator tmp = *this;_node = _node->_prev;return tmp;}//解引用Ref operator*(){return _node->_val;}//如果T是一个自定义类型可以通过it->的方式访问自定义类型成员//但实际是it->->, 编译器做了特殊处理, 自动省略掉一个->, 所以直接写it->即可//it->->的意思是: it.operator->()->, it.operator->()拿到一个指针(T*/const T*), 指针->是解引用Ptr operator->(){return &(operator*());}bool operator!=(iterator it){return _node != it._node;}      bool operator==(iterator it){return _node == it._node;}
};//list类
template<class T>
class list
{
public:typedef __list_node<T> node;typedef __list_iterator<T, T&, T*> iterator;typedef __list_iterator<T, const T&, const T*> const_iterator;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);}//......//......
private:node* _head;

3.const迭代器复用普通迭代器

为什么iterator这个类模板的模板参数是template<class T, class Ref, class Ptr>, 直接写一个class T不就可以了吗?Ref, Ptr是用来做什么的?

这块整体是一个复用的思想, 让const_iterator(const迭代器)复用到iterator(普通迭代器)

如果我要构造一个普通迭代器, 传入T, T&, T* 分别对应iterator类模板的模板参数T, Ref, Ptr

如果我要构造一个const迭代器, 传入T, const T&, T* 分别对应iterator类模板的模板参数T, Ref, Ptr

这样完美的让一份iterator类模板同时可以支持普通迭代器与const迭代器, 如果只写template<class T>, 还需要再实现一个const_iterator的类模板

三.反向迭代器

1.反向迭代器也称迭代器适配器(复用的思想)

反向迭代器是迭代器适配器

stack&&queue&&priority_queue是容器适配器, 在后面的篇章会详细讲解

反向迭代器的精髓就在于, 只需要实现一份reverse_iterator便可以适配出绝大多数的容器的反向迭代器

注意这里是绝大多数容器! 绝大多数可以定义为, 这些容器的迭代器需要支持++与--操作

换句话说, 只要是能够支持迭代器++与--的容器, 就可以使用反向迭代器reverse_iterator适配出该容器自己的反向迭代器

例如forward_list, unordered_map, unordered_set这三种容器便不支持反向迭代器, 也就是不能适配

2.反向迭代器的使用

list<int> l;
l.push_back(1);
l.push_back(2);
l.push_back(3);
l.push_back(4);
l.push_back(5);
list<int>::reverse_iterator it = l.rbegin();
while (it != l.rend())
{cout << *it << ' ';++it;
}
cout << endl;

3.反向迭代器操作原理详解

反向迭代器, 从名字上来理解, 他其实就是一个反着的正向迭代器

list: 1,2,3,4,5

正向迭代器: [begin, end]

begin --> end, it++

结果: 1,2,3,4,5

反向迭代器: [rend, rbegin]

rbegin-> rend, it++

结果: 5,4,3,2,1

反向迭代器的++操作, 在底层上是正向迭代器的--操作

rbegin()的位置是原end()的位置

rend()的位置是原begin()的位置

reverse_iterator rbegin()的实现必须是要 return iterator(begin()) 以这种形式, 才可以适配多种容器让reverse_iteartor复用iterator, rend()同理

所以在解引用时, 如上图, 是一定要先拷贝一份后(*不能改变迭代器的位置), 先--再return的

4.反向迭代器的代码实现

//迭代器适配器, 复用思想
template<class Iterator, class Ref, class Ptr>
struct Reverse_iterator
{typedef Reverse_iterator<Iterator, Ref, Ptr> rIt;Iterator _it;//构造Reverse_iterator(Iterator it):_it(it)//使用初始化列表, 如果不在这里定义的化需要在__list_iterator实现默认构造{}//其余接口//前置++rIt& operator++(){_it--;return *this;}//后置++rIt operator++(int){rIt tmp(_it);_it--;return tmp;}//前置--rIt& operator--(){_it++;return *this;}//后置--rIt operator--(int){rIt tmp(_it);_it++;return tmp;}//解引用Ref operator*(){Iterator tmp(_it);return *(--tmp);}//如果*_node是一个自定义类型可以通过it->的方式访问自定义类型成员//但实际是it->->, 编译器做了特殊处理, 自动省略掉一个->, 所以直接写it->即可//it->->的意思是: it.operator->()->, it.operator->()拿到一个指针(T*/const T*), 指针->是解引用Ptr operator->(){    //调用上面的operator*(), 底层逻辑又会去调用__list_iterator类中的operator*()//最终会拿到_node->_val//最后再取地址, 拿到_val的地址return &(operator*());}bool operator!=(rIt s){return _it != s._it;}bool operator==(rIt s){return _it == s._it;}
};//list类
template<class T>
class list
{
public:typedef __list_node<T> node;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;//......//......///反向迭代器reverse_iterator rbegin(){return reverse_iterator(end());}reverse_iterator rend(){return reverse_iterator(begin());}const_reverse_iterator rbegin() const{return const_reverse_iterator(end());}const_reverse_iterator rend() const{return const_reverse_iterator(begin());}//......//......
private:node* _head;

这里的const_reverse_iterator复用reverse_iterator与上面const_iterator复用iterator是一个道理滴!!!

5.迭代器的分类


单向迭代器(forward_iterator) 支持++

例如: forward_list, unordered_map, unordered_set


双向迭代器(bidirectional_iterator) 支持 ++ / --

例如: list, map, set


随机迭代器(random_access_iterator) 支持 ++ / -- / + / -

例如: vector, deque, string


随机迭代器是特殊的双向迭代器

可以说, 如果该容器可以适配反向迭代器, 那么该容器的迭代器就是双向迭代器


四.常用接口

以下涉及到的深拷贝与迭代器失效详解, 具体可以参考我之前写的vector模拟实现

详解入口: http://t.csdn.cn/B8cpI

1.构造函数

void InitializeList()
{_head = new node;_head->_next = _head;_head->_prev = _head;
}list()
{InitializeList();
}list(int n, const T& value = T())
{InitializeList();while (n--){push_back(value);}
}template <class Iterator>
list(Iterator first, Iterator last)
{InitializeList();while (first != last){push_back(*first);++first;}
}list(const list<T>& l)
{InitializeList();list<T> tmp(l.begin(), l.end());swap(tmp);
}

2.insert与erase

在list中

insert不会发生迭代器失效

erase必定会发生迭代器失效

注意: stl中实现的insert插入之后, 返回的是插入的节点的迭代器, 返回值更新了原迭代器位置为新插入的

erase返回值是删除节点的下一个节点的迭代器

iterator insert(iterator pos, const T& val)
{node* cur = pos._node;node* newnode = new node(val);node* pprev = cur->_prev;pprev->_next = newnode;cur->_prev = newnode;newnode->_next = cur;newnode->_prev = pprev;return iterator(newnode);
}iterator erase(iterator pos)
{node* cur = pos._node;node* pprev = cur->_prev;node* pnext = cur->_next;delete cur;cur = nullptr;pprev->_next = pnext;pnext->_prev = pprev;return iterator(pnext);
}

3.其余接口

list<T>& operator=(const list<T> l)
{list<T> tmp(l);swap(tmp);return *this;
}void swap(list<T>& l)
{::swap(_head, l._head);
}bool empty()
{return _head->_next == _head;
}void push_back(const T& x)
{//new节点node* newnode = new node(x);//尾插node* tail = _head->_prev;newnode->_next = _head;newnode->_prev = tail;tail->_next = newnode;_head->_prev = newnode;
}
void pop_back()
{node* tail = _head->_prev;node* tprev = tail->_prev;node* tnext = tail->_next;delete tail;tail = nullptr;tprev->_next = tnext;tnext->_prev = tprev;
}void push_front(const T& val)
{insert(begin(), val);
}void pop_front()
{erase(begin());
}T& front()
{return _head->_next->_val;
}
const T& front()const
{return _head->_next->_val;
}
T& back()
{return _head->_prev->_val;
}
const T& back()const
{return _head->_prev->_val;
}size_t size() const
{node* cur = _head;int count = 0;while (cur->_next != _head){count++;cur = cur->_next;}return count;
}//清理, 保留头节点
void clear()
{node* cur = _head->_next;while (cur != _head){node* next = cur->_next;delete cur;cur = next;}_head->_next = _head;_head->_prev = _head;
}//析构, 全部清理包括头节点在内
~list()
{ node* cur = _head->_next;while (cur != _head){node* next = cur->_next;delete cur;cur = next;}delete _head;_head = nullptr;
}

【STL】容器 - list的模拟实现相关推荐

  1. C++ STL容器:栈(stack)

    目录 栈的定义 栈的结构 栈的基本操作 栈的定义方式和函数调用 经典例题 数组模拟 S T L STL STL 容器 s t a c k stack stack 做法 栈的定义 先进后出的线性表. 栈 ...

  2. C++常用STL容器

    C++常用STL容器 vector 向量容器 二维数组指针.二维向量 pair 对 list 双向列表 map 表 unordered_map 哈希表 set 集合 unordered_set 哈希集 ...

  3. C++ [STL容器反向迭代器]

    本文已收录至<C++语言和高级数据结构>专栏! 作者:ARMCSKGT STL容器反向迭代器 前言 正文 适配器 反向迭代器 反向迭代器框架 默认成员函数 反向迭代器的遍历 反向迭代器的比 ...

  4. 2020牛客国庆集训派对day8 G-Shuffle Cards(扩展STL容器,rope可持久化平衡树)

    2020牛客国庆集训派对day8 G-Shuffle Cards(扩展STL容器,rope可持久化平衡树) 题目 https://ac.nowcoder.com/acm/contest/7865/G ...

  5. C++ 笔记(19)— 标准模板库(STL容器、STL迭代器、STL算法、STL容器特点、STL字符串类)

    C++ 标准库可以分为两部分: 标准函数库: 这个库是由通用的.独立的.不属于任何类的函数组成的.函数库继承自 C 语言. 面向对象类库: 这个库是类及其相关函数的集合. C++ 标准库包含了所有的 ...

  6. C++拾趣——STL容器的插入、删除、遍历和查找操作性能对比(Windows VirtualStudio)——遍历和删除

    相关环境和说明在<C++拾趣--STL容器的插入.删除.遍历和查找操作性能对比(Windows VirtualStudio)--插入>已给出.本文将分析各个容器中遍历和查找的性能.(转载请 ...

  7. C++拾趣——STL容器的插入、删除、遍历和查找操作性能对比(Windows VirtualStudio)——删除

    相关环境和说明在<C++拾趣--STL容器的插入.删除.遍历和查找操作性能对比(Windows VirtualStudio)--插入>已给出.本文将分析从头部.中间和尾部对各个容器进行删除 ...

  8. C++拾趣——STL容器的插入、删除、遍历和查找操作性能对比(ubuntu g++)——遍历和查找

    相关环境和说明在<C++拾趣--STL容器的插入.删除.遍历和查找操作性能对比(ubuntu g++)--插入>已给出.本文将分析各个容器中遍历和查找的性能.(转载请指明出于breakso ...

  9. C++拾趣——STL容器的插入、删除、遍历和查找操作性能对比(ubuntu g++)——删除

    相关环境和说明在<C++拾趣--STL容器的插入.删除.遍历和查找操作性能对比(ubuntu g++)--插入>已给出.本文将分析从头部.中间和尾部对各个容器进行删除的性能.(转载请指明出 ...

  10. STL容器存储的内容动态分配情况下的内存管理

    主要分两种情况:存储的内容是指针:存储的内容是实际对象. 看以下两段代码, typedef pair<VirObjTYPE, std::list<CheckID>*> VirO ...

最新文章

  1. centos查看历史指令记录_CentOS 系统通过 curlftpfs 挂载 FTP 服务器为本地磁盘
  2. KDD 2021多个奖项出炉:斯坦福博士摘得学位论文奖,北航校友胡侠获新星奖
  3. 关于学习Python的一点学习总结(31->继承及多态)
  4. 北京智源人工智能研究院关于征集2019年度智源青年科学家的通知
  5. 消息模式Toast.makeText的几种常见用法
  6. window.opener.document.getElementById(“a”)用标准的Jquery 语言怎么写
  7. ArcGIS实现在线与线交叉处打断线(批量)
  8. 第22条:理解NSCopying 协议
  9. [转载]Office Visio快捷键
  10. 【Python】100行Python代码轻松开发个人博客
  11. XML——XML的那些事
  12. SQL Server 2014各个版本区别
  13. DS18B20温度传感器使用简介
  14. 【路径规划】局部路径规划算法——人工势场法(含python实现)
  15. 企业微信支付提示请在微信客户端打开链接_微信h5支付?
  16. 中学计算机课小课题,【信息技术课题研究方案】 信息技术小课题研究题目大全_信息技术课题研究题目_信息技术课题研究_东城教研...
  17. 打印纸张尺寸换算_电脑打印纸规格全记录
  18. 【21CN】“电商+互动直播”聚美优品携手腾讯云开启颜值经济
  19. android 闪屏动态界面,Android实现闪屏欢迎界面
  20. 微信公众号开发 糟糕的体验_糟糕的开发人员–好老板

热门文章

  1. 空间滤波matlab
  2. 解读:百度将处理不相关静态搜索结果页
  3. 关闭TransactionSynchronizationManager下日志打印
  4. 阿朱分享:中国互联网十五年的22个创新模式(转自CSDN)
  5. python从入门到精通最全总结
  6. Android底层驱动开发 -驱动配置篇
  7. 什么是教育大数据?| 教育热点更新
  8. c++课设学生成绩与学籍管理系统
  9. 武汉北大青鸟J2EE程序员的武功修为
  10. 微调研设置问题控制器