
需要云服务器等云产品来学习Linux的同学可以移步/-->腾讯云<--/-->阿里云<--/-->华为云<--/官网,轻量型云服务器低至112元/年,新用户首次下单享超低折扣。
目录
列表是一种顺序容器,它允许在序列中的任何位置执行常量时间插入和删除操作,并允许在两个方向上进行迭代。
它的底层是一个带头双向循环链表。附一篇博主用C语言写的带头双向循环链表:【数据结构】带头双向循环链表的实现
list不能用算法库的sort进行排序。算法库中的sort的底层是一个快排,需满足三数取中,需要传入随机访问迭代器,所以list并不适用。
当然list中提供了一个自己的sort,它的底层是一个归并排序。但是这个sort比vector使用算法库的sort还慢,甚至比list的数据先push_back到vector到再用算法库的sort还要慢。

insert,迭代器不失效
earse失效
1、单向迭代器:只能++,不能--。例如单链表,哈希表;
2、双向迭代器:既能++也能--。例如双向链表;
3、随机访问迭代器:能++--,也能+和-。例如vector和string。
迭代器是内嵌类型(内部类或定义在类里)
迭代器的实现需要支持解引用(为了取数据),支持++--。
博主模拟实现string和vector时,直接将原生指针typedef成迭代器,是因为数组的结构正好满足迭代器的行为(注:string和vector可以用原生指针实现,但是vs中采用自定义类型封装的方式实现),但是list中的节点地址是不连续的,不能使用原生指针,需要使用类进行封装+运算符重载实现。
- //用类封装迭代器
- template <class T>
- struct __list_iterator
- {
- typedef list_node
node; - //用节点的指针进行构造
- __list_iterator(node* p)
- :_pnode(p)
- {}
- //迭代器运算符的重载
- T& operator*()
- {
- return _pnode->_data;
- }
- __list_iterator
& operator++()//返回值不要写成node* operator++(),因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对 - {
- //return _pnode->_next;
- _pnode=_pnode->_next;
- return *this;//返回的是迭代器
- }
- bool operator!=(const __list_iterator
& it) - {
- return _pnode != it._pnode;
- }
- public:
- node* _pnode;//封装一个节点的指针
- };

const迭代器的错误写法:
- typedef __list_iterator
iterator; - const list
::iterator it=lt.begin();
因为typedef后,const修饰的是迭代器it,只能调用operator*(),调不了operator++()。(重载operator++()为const operator++()也不行,因为const版本++还是改变不了)
正确写法:想实现const迭代器,不能在同一个类里面动脑筋,需要再写一个const版本迭代器的类。
- //用类封装const迭代器
- template <class T>
- struct __list_const_iterator
- {
- typedef list_node
node; - //用节点的指针进行构造
- __list_const_iterator(node* p)
- :_pnode(p)
- {}
- //迭代器运算符的重载
- const T& operator*()const
- {
- return _pnode->_data;
- }
- __list_const_iterator
& operator++()//返回值不要写成node*,因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对 - {
- //return _pnode->_next;//返回类型错误的
- _pnode = _pnode->_next;
- return *this;//返回的是迭代器
- }
- __list_const_iterator
& operator--() - {
- _pnode = _pnode->_prev;
- return *this;
- }
- bool operator!=(const __list_const_iterator
& it)const - {
- return _pnode != it._pnode;
- }
- public:
- node* _pnode;//封装一个节点的指针
- };
-
- typedef __list_const_iterator
const_iterator;
当然,这样写__list_iterator和__list_const_iterator这两个类会出现代码重复。STL库中是通过类模板多给一个参数来实现,这样,同一份类模板就可以生成两种不同的类型的迭代器(以下为仿STL库的模拟实现):
- //用类封装普通/const迭代器
- template <class T,class Ref>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
Self; - //用节点的指针进行构造
- __list_iterator(node* p)
- :_pnode(p)
- {}
- //迭代器运算符的重载
- Ref operator*()
- {
- return _pnode->_data;
- }
- Self& operator++()//返回值不要写成node*,因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对
- {
- //return _pnode->_next;//返回类型错误的
- _pnode=_pnode->_next;
- return *this;//返回的是迭代器
- }
- Self& operator--()
- {
- _pnode = _pnode->_prev;
- return *this;
- }
- bool operator!=(const Self& it)
- {
- return _pnode != it._pnode;
- }
- public:
- node* _pnode;//封装一个节点的指针
- };
-
- typedef __list_iterator
iterator; - typedef __list_iterator
const T&> const_iterator;
1、反向迭代器的底层
反向迭代器的本质就是对正向迭代器的封装,它是一个适配器。
STL源码中的反向迭代器的实现:反向迭代器用正向迭代器进行构造。
- reverse_iterator rbegin(){return reverse_iterator(end());}
- reverse_iterator rend(){return reverse_iterator(begin());}
画个图,其实正向迭代器和反向迭代器的位置是对称的。

通过图示,rbegin()迭代器是最后一个数据的下一个位置,但是通过rbegin()访问的数据应该是4,这是通过运算符重载operator*()来解决。
- template <class Iterator, class Ref, class Ptr>
- Ref operator*()
- {
- Iterator tmp = _it;
- return *--(tmp);
- }
- typedef ReserveIterator
reverse_iterator; - typedef ReserveIterator
const T&, const T*> const_reverse_iterator;
2、反向迭代器的实现
- namespace jly
- {
- template <class Iterator, class Ref, class Ptr>
- class ReserveIterator
- {
- public:
- ReserveIterator(Iterator it)//使用正向迭代器构造反向迭代器
- :_it(it)
- {
-
- }
- Ref operator*()
- {
- Iterator tmp = _it;
- return *--(tmp);
- }
- Ptr operator->()//operator->()返回数据的地址
- {
- return &(operator*());
- }
- ReserveIterator
& operator++()//返回值不要写成Iterator,因为反向迭代器++返回的是反向迭代器,不是传入的迭代器类型 - {
- --_it;
- return *this;
- }
- ReserveIterator
& operator--() - {
- ++_it;
- return *this;
- }
- bool operator!=(const ReserveIterator
& rit)const - {
- return _it != rit._it;
- }
- private:
- Iterator _it;//底层是传入类型的迭代器
- };
- }
1、封装底层实现,不暴露底层实现的细节;
2、多种容器提供统一的访问方式,降低使用成本;
C语言没有运算符重载和引用等语法,是实现不了迭代器的。
迭代器的用法就是模拟指针的行为,如果现在有一个指向结构的指针,那么就需要用到->来解引用。
- //*的重载:返回节点的数据
- Ref operator*()
- {
- return _pnode->_data;
- }
- //->的重载:返回数据的指针
- T* operator->()
- {
- return &_pnode->_data;
- }

但是operator->使用T*做返回值类型,这样无论是普通迭代器和const迭代器都能修改,所以operator->的返回值类型应该改为泛型:
- template <class T, class Ref,class Ptr>
- Ptr operator->()
- {
- return &_pnode->_data;
- }
- typedef __list_iterator
iterator; - typedef __list_iterator
const T&, const T*> const_iterator;

普通类:类名等于类型
类模板:类名不等价于类型,例如list类模板类名是list,类型list
所以我们平常写函数形参和返回值时,总会带上形参和返回值的类型:
- // 赋值运算符重载
- list
& operator=(list lt) - {
- swap(lt);
- return *this;
- }
但是C++在类模板里面可以用类名代替类型:
- // 赋值运算符重载写法2(赋值运算符重载都可以这么干)
- list& operator=(list lt)
- {
- swap(lt);
- return *this;
- }
建议写代码的时候严格区分类型和类名,让自己和别人能够看的很明白。(了解下C++有这种坑语法即可)
vector和list像左右手一样,是互补关系,缺一不可。vector的优点正是list的缺点,list的优点也是vector的缺点,实际选用容器时,按照需求择优选用。
vector的优点(结构牛逼):
1、支持下标的随机访问;
2、尾插尾删效率高(当然扩容的那一次尾插会较慢);
3、CPU高速缓存命中高(数据从缓存加载至CPU中,会加载连续的一段数据,vector因为结构连续,高速缓存命中高)。
vector的缺点:
1、非尾插尾删效率低;
2、扩容有消耗,并存在一定的空间浪费。
vector迭代器失效问题:
insert/erase均失效。(如果string的insert和erase形参是迭代器,那么也会失效,但是大部分接口是下标传参,不考虑失效问题,只有几个接口是迭代器传参,需要注意迭代器失效问题)
list的优点:
1、按需申请释放,无需扩容;
2、任意位置插入删除时间O(1);(这里说的是插入删除,不要加上查找的时间)
list的缺点:
1、不支持下标的随机访问;
2、CPU高速缓存命中率低;
3、每一个节点除了存储数据外,还需要额外存储两个指针。
list迭代器失效问题:
insert不失效,erase失效。
- #pragma once
- #include
- #include
- #include
- #include
- using std::cout;
- using std::endl;
- namespace jly
- {
- //链表节点的类
- template <class T>
- struct list_node
- {
- list_node(const T& x = T())//给一个缺省值,变成默认构造函数
- :_next(nullptr)
- , _prev(nullptr)
- , _data(x)
- {}
-
- list_node* _next;
- list_node* _prev;
- T _data;
- };
- //用类封装普通/const迭代器
- template <class T, class Ref,class Ptr>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
Self; - //用节点的指针进行构造
- __list_iterator(node* p)
- :_pnode(p)
- {}
- //迭代器运算符的重载
- Ref operator*()
- {
- return _pnode->_data;
- }
- Ptr operator->()
- {
- return &_pnode->_data;
- }
- Self& operator++()//返回值不要写成node*,因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对
- {
- //return _pnode->_next;//返回类型错误的
- _pnode = _pnode->_next;
- return *this;//返回的是迭代器
- }
- Self operator++(int)//后置++
- {
- _pnode = _pnode->_next;
- return Self(_pnode->_next);
- }
- Self& operator--()
- {
- _pnode = _pnode->_prev;
- return *this;
- }
- Self operator--(int)//后置--
- {
- _pnode = _pnode->_prev;
- return Self(_pnode->_prev);
- }
- bool operator!=(const Self& it)const
- {
- return _pnode != it._pnode;
- }
- bool operator==(const Self& it)const
- {
- return _pnode == it._pnode;
- }
- public:
- node* _pnode;//封装一个节点的指针
- };
- //用类封装const迭代器
- //template
- //struct __list_const_iterator
- //{
- // typedef list_node
node; - // //用节点的指针进行构造
- // __list_const_iterator(node* p)
- // :_pnode(p)
- // {}
- // //迭代器运算符的重载
- // const T& operator*()const
- // {
- // return _pnode->_data;
- // }
- // __list_const_iterator
& operator++()//返回值不要写成node*,因为迭代器++肯定返回迭代器啊,你返回节点指针类型不对 - // {
- // //return _pnode->_next;//返回类型错误的
- // _pnode = _pnode->_next;
- // return *this;//返回的是迭代器
- // }
- // __list_const_iterator
& operator--() - // {
- // _pnode = _pnode->_prev;
- // return *this;
- // }
- // bool operator!=(const __list_const_iterator
& it)const - // {
- // return _pnode != it._pnode;
- // }
- //public:
- // node* _pnode;//封装一个节点的指针
- //};
- //用正向迭代器封装反向迭代器
- template <class Iterator, class Ref, class Ptr>
- class ReserveIterator
- {
- public:
- ReserveIterator(Iterator it)//使用正向迭代器构造反向迭代器
- :_it(it)
- {
-
- }
- Ref operator*()
- {
- Iterator tmp = _it;
- return *--(tmp);
- }
- Ptr operator->()//operator->()返回数据的地址
- {
- return &(operator*());
- }
- ReserveIterator
& operator++()//返回值不要写成Iterator,因为反向迭代器++返回的是反向迭代器,不是传入的迭代器类型 - {
- --_it;
- return *this;
- }
- ReserveIterator
& operator--() - {
- ++_it;
- return *this;
- }
- bool operator!=(const ReserveIterator
& rit)const - {
- return _it != rit._it;
- }
- private:
- Iterator _it;//底层是传入类型的迭代器
- };
-
- //链表类(控制哨兵位)
- template <class T>
- class list
- {
- public:
- typedef list_node
node; - typedef __list_iterator
iterator; - typedef __list_iterator
const T&,const T*> const_iterator; - typedef ReserveIterator
reverse_iterator; - typedef ReserveIterator
const T&, const T*> const_reverse_iterator; -
- //typedef __list_const_iterator
const_iterator; - //构造函数
- void empty_initialize()//用于初始化哨兵位
- {
- _head = new node;
- _head->_next = _head;
- _head->_prev = _head;
- _size = 0;
- }
- list()
- {
- empty_initialize();
- }
- template <class InputIterator>
- list(InputIterator first, InputIterator last)//迭代器区间构造
- {
- empty_initialize();
- while (first != last)
- {
- push_back(*first);
- ++first;
- }
- }
- //析构函数
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
- //拷贝构造
- list(const list
& lt) - {
- 先初始化*this
- //empty_initialize();
- //for (const auto& e : lt)//取lt的数据给e
- //{
- // push_back(e);
- //}
-
- //工具人写法
- list
tmp(lt.begin(),lt.end()) ; - empty_initialize();
- swap(tmp);
- }
- void swap(list
& lt) - {
- std::swap(_head, lt._head);//交换头指针
- std::swap(_size,lt._size);
- }
- 赋值运算符重载写法1
- //list
& operator=(const list& lt) - //{
- // //防止自己给自己赋值
- // if (this != <)
- // {
- // clear();
- // for (const auto& e : lt)
- // {
- // push_back(e);
- // }
- // }
- // return *this;
- //}
- // 赋值运算符重载写法2(赋值运算符重载都可以这么干)
- list
& operator=(list lt) - {
- swap(lt);//直接交换
- return *this;
- }
- //插入删除
- void push_back(const T& x)
- {
- /*node* newNode = new node(x);
- node* tail = _head->_prev;
- newNode->_prev = tail;
- newNode->_next = _head;
- tail->_next = newNode;
- _head->_prev = newNode;*/
- insert(end(), x);
- }
- void pop_back()
- {
- erase(--end());
- }
- void push_front(const T& x)//头插
- {
- insert(begin(), x);
- }
- void pop_front()
- {
- erase(begin());
- }
- iterator insert(iterator pos, const T& x)
- {
- node* newNode = new node(x);
- node* prev = pos._pnode->_prev;
- node* cur = pos._pnode;
- newNode->_prev = prev;
- newNode->_next = cur;
- prev->_next = newNode;
- cur->_prev = newNode;
- //return iterator(newNode);
- pos._pnode = newNode;
- ++_size;
- return pos;
- }
- iterator erase(iterator pos)
- {
- assert(!empty());
- node* prev = pos._pnode->_prev;
- node* next = pos._pnode->_next;
- prev->_next = next;
- next->_prev = prev;
- delete pos._pnode;
- --_size;
- //return iterator(next);
- pos._pnode = next;
- return pos;
- }
- //链表小接口
- bool empty()const
- {
- return _head->_next == _head;
- }
- void clear()
- {
- /*assert(!empty);
- node* cur = _head->_next;
- while (cur != _head)
- {
- node* next = cur->_next;
- delete cur;
- cur = next;
- }*/
- iterator it = begin();
- while (it != end())
- {
- it = erase(it);//erase返回删除元素的下一个
- }
- }
- size_t size()const
- {
- return _size;
- }
- //普通begin(),end()迭代器
- iterator begin()
- {
- //return iterator(_head->_next);
- return __list_iterator
(_head->_next); - }
- iterator end()
- {
- return __list_iterator
(_head); - }
- //const begin(),end()迭代器
- const_iterator begin()const
- {
- //return const_iterator(_head->_next);
- return __list_iterator
const T&,const T*>(_head->_next); - }
- const_iterator end()const
- {
- return __list_iterator
const T&,const T*>(_head); - }
- reverse_iterator rbegin()
- {
- return reverse_iterator(end());
- }
- reverse_iterator rend()
- {
- return reverse_iterator(begin());
- }
- private:
- node* _head;//哨兵位
- size_t _size;//用于统计节点个数,空间换时间
- //不加这个私有变量,统计节点个数时间O(N),有这个私有变量,时间O(1),但是每个节点的体积变大
- };
-
-
- //测试函数
- struct Pos
- {
- int _row;
- int _col;
-
- Pos(int row = 0, int col = 0)
- :_row(row)
- , _col(col)
- {}
- };
- void test()
- {
- list
i; - i.push_back(Pos(1, 2));
- i.push_back(Pos(2, 5));
- i.push_back(Pos(4, 3));
- list
::iterator it = i.begin(); - while (it != i.end())
- {
- cout << (&( * it))->_row;//*it取数据,再取地址、解引用得到_row,多此一举
- cout << it->_row;//同第三种写法,编译器为了可读性,省略了一个->
- cout << it.operator->()->_row;//it.operator->()是显示调用,->_row是解引用得到_row
- it++;
- }
- }
- void test1()
- {
- list<int> i;
- i.push_back(1);
- i.push_back(2);
- i.push_back(3);
- list<int>::iterator it = i.begin();
- while (it != i.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- list<int>::reverse_iterator rit = i.rbegin();
- while (rit != i.rend())
- {
- cout << *rit << " ";
- ++rit;
- }
- }
- }