目录
list的底层是一个双向带头循环链表
- #pragma once
- #include
- using namespace std;
-
- namespace tutu
- {
- template<class T>
- struct listNode
- {
- listNode
* _next; - listNode
* _prev; - T _data;
-
- listNode(const T& data=T())
- :_next(nullptr)
- ,_prev(nullptr)
- ,_data(data)
- {}
- };
-
- template<class T>
- class list
- {
- typedef listNode
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;
- };
-
- void test1()
- {
- list<int> l1;
- l1.push_back(1);
- l1.push_back(2);
- l1.push_back(3);
- l1.push_back(4);
-
- }
- }
- #include"list.h"
-
- int main()
- {
- tutu::test1();
- return 0;
- }
迭代器是像指针一样的类型,模仿指针的行为,解引用能取数据,++能到下一个位置。
在string和vector中,迭代器就是原生指针,因为他们底层是连续的物理空间;但是list底层物理空间不是连续的,如果还用原生指针,解引用是一个节点,++到不了下一个位置,但是C++提供了类,自定义类型可以对运算符进行重载,所以list底层迭代器的实现就是对节点指针进行封装,重载operator++、operator!=和operator*。
- #pragma once
- #include
- using namespace std;
-
- namespace tutu
- {
- template<class T>
- struct listNode
- {
- listNode
* _next; - listNode
* _prev; - T _data;
-
- listNode(const T& data=T())
- :_next(nullptr)
- ,_prev(nullptr)
- ,_data(data)
- {}
- };
-
- template<class T>
- struct __list_iterator
- {
- typedef listNode
Node; - Node* _node;
-
- __list_iterator(Node* node)
- :_node(node)
- {}
-
- //++it
- __list_iterator
& operator++() - {
- _node = _node->_next;
- return *this;
- }
-
- //it++
- __list_iterator
operator++(int) - {
- __list_iterator
tmp(*this) ; - _node = _node->_next;
- return tmp;
- }
-
- //--it
- __list_iterator
& operator--() - {
- _node = _node->_prev;
- return *this;
- }
-
- //it--
- __list_iterator
operator--(int) - {
- __list_iterator
tmp(*this) ; - _node = _node->_prev;
- return tmp;
- }
-
- T& operator*()
- {
- return _node->_data;
- }
-
- bool operator!=(const __list_iterator
& it) const - {
- return _node != it._node;
- }
-
- bool operator==(const __list_iterator
& it) const - {
- return _node == it._node;
- }
-
- };
-
- template<class T>
- class list
- {
- typedef listNode
Node; - public:
- typedef __list_iterator
iterator; -
- iterator begin()
- {
- 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 test1()
- {
- list<int> l1;
- l1.push_back(1);
- l1.push_back(2);
- l1.push_back(3);
- l1.push_back(4);
- l1.push_back(5);
-
- list<int>::iterator it = l1.begin();
- while (it != l1.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- }
- }
- #include"list.h"
-
- int main()
- {
- tutu::test1();
- return 0;
- }
答:都不需要。这里就是需要浅拷贝,默认生成的即可。迭代器中虽然有一个节点指针的成员,但是这个成员不归迭代器管,迭代器的意义就是访问和修改容器的。迭代器是借助节点的指针访问修改链表,节点是属于链表的,不属于迭代器,所以不归它管释放。
Node*原生指针和一个迭代器对象,它们占用的空间是一样大的,在32位机器上都是4个byte,并且值存的也一样,但是对他们使用运算符的意义和结果是不一样的。
普通对象调普通迭代器,const对象调const迭代器,目前我们只实现了普通迭代器,const迭代器还需要再写一份,传统的写法是再写一份const版本的迭代器,但是这样会造成代码冗余,这里推荐高手写法,即stl底层实现写法。
比较一下普通迭代器和const迭代器的区别,普通迭代器可读可写,const迭代器只能读不能写,体现到代码层面上就是operator*,一个是返回引用,一个是返回const引用,所以这里用了一个模板参数解决这个问题。
- #pragma once
- #include
- using namespace std;
-
- namespace tutu
- {
- template<class T>
- struct listNode
- {
- listNode
* _next; - listNode
* _prev; - T _data;
-
- listNode(const T& data=T())
- :_next(nullptr)
- ,_prev(nullptr)
- ,_data(data)
- {}
- };
-
- template<class T,class Ref>
- struct __list_iterator
- {
- //因为增加了一个模板参数,所以下面的类型都要改,所以增加一个typedef
- typedef __list_iterator
self; - typedef listNode
Node; - Node* _node;
-
- __list_iterator(Node* node)
- :_node(node)
- {}
-
- //++it
- self& operator++()
- {
- _node = _node->_next;
- return *this;
- }
-
- //it++
- self operator++(int)
- {
- __list_iterator
tmp(*this) ; - _node = _node->_next;
- return tmp;
- }
-
- //--it
- self& operator--()
- {
- _node = _node->_prev;
- return *this;
- }
-
- //it--
- self operator--(int)
- {
- self tmp(*this);
- _node = _node->_prev;
- return tmp;
- }
-
- Ref operator*()
- {
- return _node->_data;
- }
-
- bool operator!=(const self& it) const
- {
- return _node != it._node;
- }
-
- bool operator==(const self& it) const
- {
- return _node == it._node;
- }
-
- };
-
- template<class T>
- class list
- {
- typedef listNode
Node; - public:
- typedef __list_iterator
iterator; - typedef __list_iterator
const T&> const_iterator; -
- const_iterator begin() const
- {
- return const_iterator(_head->_next);
- }
-
- const_iterator end() const
- {
- return const_iterator(_head);
- }
-
- iterator begin()
- {
- 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 Print_list(const list<int>& lt)
- {
- list<int>::const_iterator it = lt.begin();
- while (it != lt.end())
- {
- cout << *it << " ";
- ++it;
- }
- cout << endl;
- }
-
- void test1()
- {
- list<int> l1;
- l1.push_back(1);
- l1.push_back(2);
- l1.push_back(3);
- l1.push_back(4);
- l1.push_back(5);
-
- Print_list(l1);
-
- }
- }
- #include"list.h"
-
- int main()
- {
- tutu::test1();
- return 0;
- }
前面都是对list

迭代器是像指针一样的类型,所以可以对operator->进行重载,可以得到如下代码:

在__list_iterator中,对operator->进行重载,如:
- T* operator->()
- {
- return &_node->_data;
- }
注意:

然后对于operator->的返回值和operator*有着相同的问题,普通迭代器返回Date*,const迭代器返回const Date*,所以上述不能给T*,这里建议新增模板参数Ptr,传参时,普通迭代器传T*,const迭代器传const T*,如:
- #pragma once
- #include
- using namespace std;
-
- namespace tutu
- {
- template<class T>
- struct listNode
- {
- listNode
* _next; - listNode
* _prev; - T _data;
-
- listNode(const T& data=T())
- :_next(nullptr)
- ,_prev(nullptr)
- ,_data(data)
- {}
- };
-
- template<class T,class Ref,class Ptr>
- struct __list_iterator
- {
- //因为增加了一个模板参数,所以下面的类型都要改,所以增加一个typedef
- typedef __list_iterator
self; - typedef listNode
Node; - Node* _node;
-
- __list_iterator(Node* node)
- :_node(node)
- {}
-
- //++it
- self& operator++()
- {
- _node = _node->_next;
- return *this;
- }
-
- //it++
- self operator++(int)
- {
- __list_iterator
tmp(*this) ; - _node = _node->_next;
- return tmp;
- }
-
- //--it
- self& operator--()
- {
- _node = _node->_prev;
- return *this;
- }
-
- //it--
- self operator--(int)
- {
- self tmp(*this);
- _node = _node->_prev;
- return tmp;
- }
-
- Ref operator*()
- {
- return _node->_data;
- }
-
- Ptr operator->()
- {
- return &_node->_data;
- }
-
- bool operator!=(const self& it) const
- {
- return _node != it._node;
- }
-
- bool operator==(const self& it) const
- {
- return _node == it._node;
- }
-
- };
-
- template<class T>
- class list
- {
- typedef listNode
Node; - public:
- typedef __list_iterator
iterator; - typedef __list_iterator
const T&,const T*> const_iterator; -
- const_iterator begin() const
- {
- return const_iterator(_head->_next);
- }
-
- const_iterator end() const
- {
- return const_iterator(_head);
- }
-
- iterator begin()
- {
- 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;
- };
-
- struct Date
- {
- int _year;
- int _month;
- int _day;
-
- Date(int year = 1,int month = 1,int day=1)
- :_year(year)
- ,_month(month)
- ,_day(day)
- {}
- };
-
- void Print_list2(const list
& lt) - {
- list
::const_iterator it = lt.begin(); - while (it != lt.end())
- {
- cout << it->_year << "/" << it->_month << "/" << it->_day << endl;
- ++it;
- }
- cout << endl;
- }
-
- void test2()
- {
- list
l1; - l1.push_back(Date(2022, 11, 23));
- l1.push_back(Date(2022, 11, 24));
- l1.push_back(Date(2022, 11, 25));
- l1.push_back(Date(2022, 11, 26));
-
- Print_list2(l1);
- }
- }

有了insert,push_back和push_front可以复用:


有了erase,pop_back和pop_front就可以复用:

vector是连续的物理空间,是优势,也是劣势。
优势:支持高效的随机访问。
劣势:1、空间不够要增容,增容代价比较大 2、可能存在一定的空间浪费,按需申请,会导致频繁增容,所以都会扩2倍左右 3、头部或中部插入删除需要挪动数据,效率低下
list很好解决了vector以上的问题
1、按需申请释放空间 2、list任意位置支持O(1)插入删除
小结:vector和list是互补的两个数据结构。




1、支持迭代器区间初始化的构造函数

2、拷贝构造

3、赋值重载

n个val初始化的构造函数:

目前我们的代码中,已经写了支持一段迭代器区间初始化和n个val初始化的构造函数,但是当写以下代码时,编译器会报错:
![]()
这就是经典的,迭代器区间初始化和n个val初始化的构造函数的冲突问题:

小结:1、整形的字面常量默认为int类型的,浮点数的字面常量默认为double类型。 2、编译器匹配的原则:只会找更匹配的。
法一:将size_t n改为int n
法二:提供一个int n的重载版本
反向迭代器跟正向迭代器的区别就是++、--的方向是反的,所以反向迭代器封装正向迭代器即可,控制重载++、--的方向。
list库里的底层rbegin、rend跟begin、end是对称的。所以operator*取前一个位置,主要就是为了让反向迭代器开始和结束跟正向迭代器对称。

因为反向迭代器是对正向迭代器的一个封装,所以可以做成模板,iterator是哪个容器的迭代器,reverse_iterator
reverse_iterator.h
- #pragma once
- namespace tutu
- {
- template<class Iterator,class Ref,class Ptr>
- class reverse_iterator
- {
- typedef reverse_iterator
self; - public:
- reverse_iterator(Iterator it)
- :_it(it)
- {}
-
- //++rit
- self operator++()
- {
- --_it;
- return *this;
- }
-
- //--rit
- self operator--()
- {
- ++_it;
- return *this;
- }
-
- bool operator!=(const self& rit) const
- {
- return _it != rit._it;
- }
-
- Ref operator*()
- {
- Iterator prev = _it;
- return *--prev;
- }
-
- Ptr operator->()
- {
- return &operator*();
- }
-
- private:
- Iterator _it;
- };
- }
在list.h中包一下上述头文件,再增加两行typedef,就可以实现反向迭代器


我们这里有三个模板参数,而库里只有一个,这里是做了简化,因为库里的比较麻烦。
注意:
1、一个类里面要取它里面的东西,只能取它的成员变量和内嵌类型,模板参数可通过typedef取到。
2、要取模板参数中的内嵌类型,编译器这里肯定是通不过的,因为编译器编译到这里还没有实例化,凡是要取一个模板参数里的内嵌类型(内部类或typedef的),加个typename是告诉编译器,它是一个类型,先让它通过,等实例化时,再去找它里面的类型。
3、如果上述按照库中实现,定义一个模板参数,在__list_iterator定义了内嵌类型,但是vector、string就不能直接套,它们底层是原生指针,不是自定义类型,所以还是三个模板参数比较好。vector的迭代器是原生指针,无法取内部类型,那么上面的实现就完了,stl源代码中是使用了一个叫迭代器萃取的技术解决的。