list 是一个带头的双向循环链表
参数用匿名对象,方便直接构造结点。
- template<class T>
- struct list_node
- {
- list_node
* _next; - list_node
* _prev; - T _data;
-
- list_node(const T& val = T())
- :_next(nullptr)
- ,_prev(nullptr)
- ,_data(val)
- {}
- };
迭代器本质上是结点的指针,但是 list 结点不是连续的,因此原生指针的特性必须由我们自己实现
迭代器是指针,结点不属于自己,不能释放结点,因此没有析构函数
参照源码逻辑:这里是写一个迭代器的模板,下面传参时可以根据类型的不同,来确定是实例化普通的迭代器还是 const 类型的迭代器。
用 self 代替模板,方便模板参数类型的替换
Ref 和 Ptr
注意 * 和 -> 这两个符号的重载:it 是一个 int*
*it = 10;
* 是对指针解引用,直接可以改变数据,因此返回的是数据的引用;
it->data;
-> 直接对指针指向的内容进行访问,返回的应该是存储数据的地址
而引用 Ref 和 Ptr 的目的就是为了区分迭代器类型
- template<class T, class Ref, class Ptr>
- struct _list_iterator
- {
- typedef list_node
Node; - typedef _list_iterator
self; - Node* _node;
-
- //没有析构函数--结点不属于迭代器,不能被迭代器释放
- //拷贝构造和赋值重载--默认生成的浅拷贝就可以
- _list_iterator(Node* node)
- :_node(node)
- {}
-
- //T& 或者 const T&
- Ref operator*()
- {
- return _node->_data;
- }
-
- //T* 或者 const T*
- Ptr operator->()
- {
- return &(operator*());
- }
-
- self& operator++() //前置++
- {
- _node = _node->_next;
- return *this;
- }
-
- self& operator++(int) //后置++
- {
- self tmp(*this);
- _node = _node->_next;
- return tmp;
- }
-
- self& operator--() //前置--
- {
- _node = _node->_prev;
- return *this;
- }
-
- self& operator--(int) //后置--
- {
- self tmp(*this);
- _node = _node->_prevt;
- return tmp;
- }
-
- bool operator!=(const self& it)
- {
- return _node != it._node;
- }
-
- bool operator==(const self& it)
- {
- return _node == it._node;
- }
-
- };
在 list 中,通过控制模板参数的不同,来完成传参时,对不同迭代器的实例化。
- template<class T>
- class list
- {
- typedef list_node
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;
- }
-
- 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;
- }
在构造 list 时,指定迭代器的三个模板参数类型,将 const 和普通迭代器区分开来;
例如当我们想打印出一个 list 中的数据时,给打印函数传过去的是 const list
那么此时用到 const_iterator 匹配到的就是如下模板:
typedef _list_iteratorconst T&,const T*> const_iterator;
这样构建出的迭代器就不能修改数据了。
list 带头双向循环链表,我们在构造时主要是开辟头结点,令前后指针指向自身
empty_init 是初始化一个 list 的头结点
支持迭代器区间构造
现代写法完成拷贝构造
- list()
- {
- _head = new Node();
- _head->_next = _head;
- _head->_prev = _head;
- }
-
- //拷贝构造传统写法
- //list(const list
& lt) - //{
- // _head = new Node();
- // _head->_next = _head;
- // _head->_prev = _head;
- // for(auto e: lt)
- // {
- // push_back(e);
- // }
- //}
- //
-
- void empty_init()
- {
- _head = new Node();
- _head->_next = _head;
- _head->_prev = _head;
-
- }
-
- template<class InputIterator>
- list(InputIterator first,InputIterator last)
- {
- empty_init();
- while(first != last)
- {
- push_back(*first);
- ++first;
- }
- }
-
- void swap(list
& lt) - {
- std::swap(_head,lt._head);
- }
-
- //现代写法
- list(const list
& lt) - {
- empty_init();
- list
tmp(lt.begin(),lt.end()) ; - swap(tmp);
- }
-
- list
& operator=(list lt) - {
- swap(lt);
- return *this;
- }
尾插尾删和头插头删可以复用插入和删除
插入时总是插入在指定位置的前面,不会造成迭代器失效的问题
删除会造成迭代器失效,野指针问题,返回的是删除位置的下一个位置
- void push_back(const T& x)
- {
- insert(end(),x);
- }
-
- void push_front(const T& x)
- {
- insert(begin(),x);
- }
-
- void pop_back()
- {
- erase(--end());
- }
-
- void pop_front()
- {
- erase(begin());
- }
-
- //插入在pos位置之前
- void insert(iterator pos,const T& x)
- {
- Node* newnode = new Node(x);
- Node* cur = pos._node;
- Node* prev = cur->_prev;
- newnode->_next = cur;
- cur->_prev = newnode;
- prev->_next = newnode;
- newnode->_prev = prev;
- }
-
- iterator erase(iterator pos)
- {
- assert(pos != end());
- Node* cur = pos._node;
- Node* prev = cur->_prev;
- Node* next = cur->_next;
- prev->_next = next;
- next->_prev = prev;
- delete cur;
- return next;
- }
调用析构函数前,先要将数据删除,再释放头结点
clear 可以删除全部结点,刚好保留头结点
- ~list()
- {
- clear();
- delete _head;
- _head = nullptr;
- }
-
- void clear()
- {
- iterator it = begin();
- while(it != end())
- {
- it = erase(it);
- }
- }