父母就像迭代器,封装了他们的脆弱......

手撕list目录:
4.1 insert(参数必须加引用,担心非内置类型)和erase
list 是可以在常数范围内在任意位置进行插入和删除的序列式容器,其底层是带头双向循环链表;list 常用接口的使用和 string、vector 系列容器的接口使用一样,这里我不详细介绍,请看我们的老朋友:cplusplus.com - The C++ Resources Network

构造函数:
| 构造函数(constructor) | 接口说明 |
| list (size_type n, const value_type& val = value_type()) | 构造的 list 中包含n个值为val的元素 |
| list() | 构造空的 list |
| 构造空的 list | 拷贝构造函数 |
| list (InputIterator first, InputIterator last) | 用 [first, last) 区间中的元素构造 list |

增删查改:
| 函数说明 | 接口说明 |
| push_front | 在list首元素前插入值为val的元素 |
| pop_front | 删除list中第一个元素 |
| push_back | 在list尾部插入值为val的元素 |
| pop_back | 删除list中最后一个元素 |
| insert | 在list position 位置中插入值为val的元素 |
| erase | 删除list position位置的元素 |
| swap | 交换两个list中的元素 |
| clear | 清空list中的有效元素 |
注意:
1、由于 list 的物理结构是非连续的 – 前一个节点地址和后一个节点地址的位置关系是随机的,所以 list 不支持随机访问,自然也就不支持 [ ] 操作;
2、list 不支持reserve操作,因为 list 的节点是使用时开辟,使用完销毁,不能预留空间;
(从这个特点也容易看出来,如果需要一直插入删除元素,利用list更好)
除了上述 STL 容器基本都有的一般接口外,list 还提供一些独有的特殊操作接口,如下:
| 函数声明 | 接口说明 |
|---|---|
| splice | 将 list1 中的元素转移到 list2 中 |
| remove | 移除 list 中的指定元素 |
| unique | 链表去重(sort之后才可以用) |
| merge | 合并两个链表 |
| sort | 链表排序(探究为什么list自己写sort) |
| reverse | 链表逆置 |
题外话: 为什么list需要自己实现sort接口??难道说库中的封装性不好?效率不高?

我们先使用库中自己的sort函数:

我们使用算法库中的sort函数:
- void test_sort()
- {
- list<int> l1{ 5,6,4,8,9,2,7 };//C++ 11写法
- sort(l1.begin(),l1.end());
- for(auto l : l1)
- {
- cout << l << " ";
- }
- cout << endl;
- }

报错了(意外之中,如果不报错我还写这个知识点干啥 doge) 报错原因说没有-迭代器
让我们看看sort源码~

这一切的一切都是因为sort的迭代器引起的!!
注意:
1、链表排序只能使用 list 提供的 sort 接口,而不能使用 algorithm 提供的 sort 接口,因为链表物理地址不连续,迭代器为双向迭代器,不支持 + - 操作,而算法库中的 sort 函数需要支持 + - 的随机迭代器;
2、链表去重之前必须保证链表有序,否则去重不完全;
3、两个有序链表合并之后仍然保存有序;
最后,虽然 list 提供了这些具有特殊功能的接口,它们也确实有一定的作用,但是实际上这些特殊接口使用频率非常低,包括 sort 接口 (链表排序的效率太低)。
虽然链表排序只能使用 list 提供的 sort 接口,而不能使用 algorithm 提供的 sort 接口,但是其使用频率仍然非常低,这是由于链表排序的效率太低了,我们可以通过对比两组测试数据来直观的感受链表排序的效率。
测试一:vector 排序与 list 排序性能对比
- //vector sort 和 list sort 性能对比 -- release 版本下
- void test_op1() {
- srand((size_t)time(0));
- const int N = 1000000; //100万个数据
-
- vector<int> v;
- v.reserve(N);
- list<int> lt;
- for (int i = 0; i < N; ++i)
- {
- auto e = rand();
- v.push_back(e);
- lt.push_back(e);
- }
-
- //vector sort
- int begin1 = clock();
- sort(v.begin(), v.end());
- int end1 = clock();
-
- //list sort
- int begin2 = clock();
- lt.sort();
- int end2 = clock();
-
- printf("vector sort:%d\n", end1 - begin1);
- printf("list sort:%d\n", end2 - begin2);
- }

测试二:list 直接进行排序与将数据拷贝到 vector 中使用 vector 排序后再将数据拷回 list 中性能对比
- //list sort 与 将数据转移到 vector 中进行排序后拷贝回来性能对比 -- release 版本下
- void test_op2()
- {
- srand(time(0));
- const int N = 1000000; //100万个数据
- list<int> lt1;
- list<int> lt2;
- for (int i = 0; i < N; ++i)
- {
- auto e = rand();
- lt1.push_back(e);
- lt2.push_back(e);
- }
-
- //list sort -- lt1
- int begin1 = clock();
- lt1.sort();
- int end1 = clock();
-
- // 将数据拷贝到vector中排序,排完以后再拷贝回来 -- lt2
- int begin2 = clock();
- vector<int> v;
- v.reserve(N);
- for (auto e : lt2) //拷贝
- {
- v.push_back(e);
- }
- sort(v.begin(), v.end()); //排序
- lt2.assign(v.begin(), v.end()); //拷贝
- int end2 = clock();
-
- printf("list1 sort:%d\n", end1 - begin1);
- printf("list2 sort:%d\n", end2 - begin2);
- }

可以看到,list sort 的效率远低于 vector sort,甚至于说,直接使用 list sort 的效率都不如先将数据拷贝到 vector 中,然后使用 vector sort,排序之后再将数据拷贝回 list 中快;所以list中的sort接口是很挫的!!
迭代器的价值在于封装底层的实现,不具体暴露底层的实现细节,提供统一的访问方式
iterator只是代言人!!真正的牛逼大佬其实是_list_iterator

为什么在 list 中将迭代器搞成指针这招不好用了呢??
在数组中,*指针就是元素,指针++就是 +sizeof(T) 对象大小,没办法,谁叫他们物理空间连续,结构NB,所以对于vector和string类而言,物理空间是连续的,原生的指针就是迭代器了,解引用就是数据了。但是对于这里的list而言,空间是不连续的
解决方法:
此时如果解引用是拿不到数据的(空间不连续),更不用说++指向下一个结点了。所以,对于list的迭代器,原生指针已经不符合我们的需求了,我们需要去进行特殊处理:进行类的封装。我们可以通过类的封装以及运算符重载支持,这样就可以实现像内置类型一样的运算符
迭代器的俩个特征:
1.解引用2.++ / --
运算符重载的大任务:
实现解引用operator*()和++函数
按照迭代器的功能,迭代器一共可以分为以下三类:
- 单向迭代器 – 迭代器仅仅支持 ++ 和解引用操作(单链表,哈希)
- 双向迭代器 – 迭代器支持 ++、-- 和解引用操作,但不支持 +、- 操作(list 双向链表)
- 随机迭代器 – 迭代器不仅支持 ++、-- 和解引用操作,还支持 +、- 操作,即迭代器能够随机访问(string,vector)
这也充分说明,vector和string是可以用库中的sort函数的
迭代器还可以分成普通迭代器和const迭代器俩类:
- //1.const T* p1
- list<int>::const_iterator cit = lt.begin();
- //2.T* const p2
- const list<int>::iterator cit = lt.begin();
- //不符合const迭代器的行为,因为保护迭代器本身不能修改,那么我们也就不能++迭代器
灵魂拷问:const迭代器是p1还是p2?p1
const迭代器类似p1的行为,保护指向的对象不被修改,迭代器本身可以修改
vector迭代器失效:insert扩容+erase的时候会失效
和 vector 不同,list 进行 insert 操作后并不会产生迭代器失效问题,因为 list 插入的新节点是动态开辟的,同时由于 list 每个节点的物理地址是不相关的,所以插入的新节点并不会影响原来其他节点的地址;
但是 list erase 之后会发生迭代器失效,因为 list 删除节点会直接将该节点释放掉,此时我们再访问该节点就会造成越界访问
我们知道,迭代器是类似于指针一样的东西,即迭代器要能够实现指针相关的全部或部分操作 – ++、–、*、+、-;对我们之前 string 和 vector 的迭代器来说,迭代器就是原生指针,所以它天然的就支持上述操作;
但是对于 list 来说,list 的节点是一个结构体,同时 list 每个节点的物理地址是不连续的,如果此时我们还简单将节点的指针 typedef 为迭代器的话,那么显然它是不能够实现解引用、++ 等操作的,所以我们需要用结构体/类来对迭代器进行封装,再配合运算符重载等操作让迭代器能够实现解引用、++、-- 等操作
框架代码如下:
- //节点定义
- template <class T>
- struct __list_node {
- typedef void* void_pointer;
- void_pointer next;
- void_pointer prev;
- T data;
- };
-
- //迭代器定义
- typedef __list_iterator
iterator; - typedef __list_iterator
const T&, const T*> const_iterator; -
- //迭代器类
- template<class T, class Ref, class Ptr>
- struct __list_iterator {
- typedef __list_iterator
self; - typedef __list_node
* link_type; //节点的指针 - link_type node; //类成员变量
-
- __list_iterator(link_type x) : node(x) {} //将节点指针构造为类对象
-
- //... 使用运算符重载支持迭代器的各种行为
- self& operator++() {...}
- self& operator--() {...}
- Ref operator*() const {...}
- };
- namespace lzy
- {
- //结点
- template<class T>
- struct list_node
- {
- list_node* _next;
- list_node* _prev;
- T _data;
-
- list_node(const T& x)//节点的构造函数及初始化列表
- :_next(nullptr)
- , _prev(nullptr)
- , _data(x)
- {}
- };
-
- template<class T>
- class list
- {
- typedef list_node
node; - public:
- //迭代器
- typedef __list_iterator
iterator; - typedef __list_const_iterator
const_iterator; - //构造
- list()
- {
- _head = new node(T());
- _head->_next = _head;
- _head->_prev = _head;
- }
- private:
- node* _head;
- size_t _size;
- };
- }
迭代器的实现我们需要去考虑普通迭代器和const迭代器。这两种迭代器的不同,也会带来不同的接口。我们可以分别单独去进行实现,我们先来看一看简单的构造迭代器,只需要提供一个结点即可,看一看实现的基本框架:
- template<class T>
- struct __list_iterator
- {
- typedef list_node
node; - node* _pnode;
-
- __list_iterator(node* p)
- :_pnode(p)
- {}
- }
为什么迭代器不写拷贝构造函数?浅拷贝真的可以吗?
对于迭代器的拷贝构造和赋值重载我们并不需要自己去手动实现,编译器默认生成的就是浅拷贝,而我们需要的就是浅拷贝,这也说明了,并不是说如果有指针就需要我们去实现深拷贝,而且迭代器不需要写析构函数,所以说不需要深拷贝
为什么聊这个问题?因为list
这个比较简单,就是要获取迭代器指向的数据,并且返回数据的引用:
- T& operator*()
- {
- return _pnode->_data;
- }
- __list_iterator
& operator++() - {
- _pnode = _pnode->_next;
- return *this;
- }
-
- __list_iterator
& operator--() - {
- _pnode = _pnode->_prev;
- return *this;
- }
-
- bool operator!=(const __list_iterator
& it) - {
- return _pnode != it._pnode;
- }
如果按照上面的做法,我们在来看看此时普通迭代器和const迭代器的区别:
- //typedef __list_iterator
iterator; - //typedef __list_const_iterator
const_iterator; - template<class T>
- struct __list_iterator
- {
- typedef list_node
node; - node* _pnode;
-
- __list_iterator(node* p)
- :_pnode(p)
- {}
-
- T& operator*()
- {
- return _pnode->_data;
- }
-
- __list_iterator
& operator++() - {
- _pnode = _pnode->_next;
- return *this;
- }
-
- __list_iterator
& operator--() - {
- _pnode = _pnode->_prev;
- return *this;
- }
-
- bool operator!=(const __list_iterator
& it) - {
- return _pnode != it._pnode;
- }
- };
-
- //跟普通迭代器的区别:遍历,不能用*it修改数据
- template<class T>
- struct __list_const_iterator
- {
- typedef list_node
node; - node* _pnode;
-
- __list_const_iterator(node* p)
- :_pnode(p)
- {}
-
- const T& operator*()
- {
- return _pnode->_data;
- }
-
- __list_const_iterator
& operator++() - {
- _pnode = _pnode->_next;
- return *this;
- }
-
- __list_const_iterator
& operator--() - {
- _pnode = _pnode->_prev;
- return *this;
- }
-
- bool operator!=(const __list_const_iterator
& it) - {
- return _pnode != it._pnode;
- }
- };
代码冗余!!!代码冗余!!!代码冗余!!!
如果是这样子去实现的话,我们就会发现,这两个迭代器的实现并没有多大的区别,唯一的区别就在于operator*的不同。const迭代器和普通迭代器的唯一区别就是普通迭代器返回T&,可读可写,const迭代器返回const T&,可读不可写。我们可以参考源码的实现:类模板参数解决这个问题,这也是迭代器的强大之处
- template <class T,class Ref,class Ptr>
- //typedef __list_iterator
iterator; - //typedef __list_iterator
const_iterator;
利用类模板参数修正之后的代码:
- // typedef __list_iterator
iterator; - // typedef __list_iterator
const_iterator; - template<class T, class Ref, class Ptr>
- struct __list_iterator
- {
- typedef list_node
node; - typedef __list_iterator
Self; - node* _pnode;
- __list_iterator(node*p)
- :_pnode(p)
- {
- }
- //返回数据的指针
- Ptr operator->()
- {
- return &_pnode->_data;
- }
- //模板参数做返回值
- Ref operator *()
- {
- return _pnode->_data;
- }
-
- //++it
- Self& operator ++()
- {
- _pnode = _pnode->_next;
- return *this;
- }
-
- //it++
- Self operator ++(int)
- {
- Self tmp(*this);
- _pnode = _pnode->_next;
- return tmp;
- }
-
- Self& operator--()
- {
- _pnode = _pnode->_prev;
- return *this;
- }
-
- Self operator--(int)
- {
- Self tmp(*this);
- _pnode = _pnode->_prev;
- return tmp;
- }
-
- bool operator !=(const Self& it)const
- {
- return _pnode != it._pnode;
- }
-
- bool operator ==(const Self& it)const
- {
- return _pnode == it._pnode;
- }
- };
同一个类模板,此时我们传递不同的参数实例化成不同的迭代器了!!!这解决了我们刚刚所说的代码冗余问题
insert:在pos位置上一个插入,返回插入位置的迭代器,对于list的insert迭代器不会失效,vector失效是因为扩容导致pos位置造成野指针问题
- iterator insert(iterator pos,const T& x)
- {
- node* newnode = new node(x);
- node* cur = pos._pnode;
- node* prev = cur->_prev;
-
- newnode->_prev = prev;
- prev->_next = newnode;
- newnode->_next = cur;
- cur->_prev = newnode;
-
- ++_size;
- return iterator(newnode);
- }

erase:这里的带头(哨兵位)头结点不可删除,返回值是删除位置的下一个,对于list的erase迭代器是失效的
- iterator erase(iterator pos)
- {
- assert(pos != end());
- node* prev = pos._pnode->_prev;
- node* next = pos._pnode->_next;
-
- prev->_next = next;
- next->_prev = prev;
- delete pos._pnode;
- --_size;
- return iterator(next);
- }
- void push_back(const T& x)
- {
- insert(end(), x);
- }
-
- void push_front(const T& x)
- {
- insert(begin(), x);
- }
注意!list的begin和end的位置

同时这个问题还可以延伸出另一个问题:为什么迭代器访问元素的时候要这样写?
在vector中,物理地址是连续的,这么写还情有可原,分析过list的begin和end之后,你还敢这么写吗??


直接就报错了,所以正确的应该是!=,而不是 <
- void test3()
- {
- vector<int> vv={1,5,7,8,9,3,4};
- list<int> l={1,5,6,7};
- vector<int>::iterator it1=vv.begin();
- list<int>::iterator it2=l.begin();
- while(it1 < vv.end())
- {
- cout << *it1 << " ";
- it1++;
- }
- cout << endl;
- // while(it2 < l.end())
- // {
- // cout << *it2 << " ";
- // it2++;
- // }
- while(it2 != l.end())
- {
- cout << *it2 << " ";
- it2++;
- }
- cout << endl;
- }
尾删和头删,复用erase即可
- void pop_front()
- {
- erase(begin());
- }
-
- void pop_back()
- {
- erase(--end());
- }
这里的尾删刚好用上了我们的重载
默认构造:
- list()
- {
- _head = new node(T());
- _head->_next = _head;
- _head->_prev = _head;
- _size = 0;
- }
我们可以用empty_initialize()来封装初始化,方便复用,不用每次都写:
- void empty_initialize()
- {
- _head = new node(T());
- _head->_next = _head;
- _head->_prev = _head;
- _size = 0;
- }
迭代器区间构造:
- //迭代器区间构造
- template <class InputIterator>
- list(InputIterator first, InputIterator last)
- {
- empty_initialize();
- while (first != last)
- {
- push_back(*first);
- ++first;
- }
- }
拷贝构造:
传统:
- list(const list
& lt) - {
- empty_initialize();
- for (const auto& e : lt)
- {
- push_back(e);
- }
- }
用范围for进行尾插,但是要注意要加上&,范围for是*it赋值给给e,又是一个拷贝,e是T类型对象,依次取得容器中的数据,T如果是string类型,不断拷贝,push_back之后又销毁
现代:
- void swap(list
& lt) - {
- std::swap(_head, lt._head);
- std::swap(_size, lt._size);
- }
- list(const list
& lt) - {
- empty_initialize();
- list
tmp(lt.begin(), lt.end()) ; - swap(tmp);
- }
- list
& operator=(list lt) - {
- swap(lt);
- return *this;
- }
查看官方文档,我们可以看到list没有类型:

list
list& operator=(list lt)
对于普通类:类名等价于类型
对于类模板:类名不等价于类型(如list模板,类名:list 类型:list)
类模板里面可以用类名代表类型,但是并不建议,在类外面则必须要带模板参数list
| vector | list | |
| 底层结构 | 动态顺序表,一段连续空间 | 带头结点的双向循环链表 |
| 随机访问 | 支持随机访问,访问某个元素效率 O(1) | 不支持随机访问 |
| 插入和删除 | 任意位置插入和删除效率低,需要搬移元素,时间复杂度为 O(N),插入时有可能需要增容,增容:开辟新空间,拷贝元素,释放旧空间,导致效率更低 | 任意位置插入和删除效率高,不需要搬移元素,时间复杂度为 O(1) |
| 空间利用率 | 底层为连续空间,不容易造成内存碎片,空间利用率高,缓存利用率高 | 底层节点动态开辟,小节点容易造成内存碎片,空间利用率低,缓存利用率低 |
| 迭代器 | 原生态指针 | 对原生态指针 (节点指针) 进行封装 |
| 迭代器失效 | 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 | 插入元素不会导致迭代器失效,删除元素时,只会导致当前迭代器失效,其他迭代器不受影响 |
| 使用场景 | 在插入元素时,要给所有的迭代器重新赋值,因为插入元素有可能会导致重新扩容,致使原来迭代器失效,删除时,当前迭代器需要重新赋值否则会失效 | 大量插入和删除操作,不关心随机访问 |
- #pragma once
-
- #include
- #include
- #include
-
- namespace lzy {
- template<class T>
- struct list_node //list 节点结构定义
- {
- list_node
* _next;//不加也没错,但是写上好一些 - list_node
* _prev; - T _data;
-
- list_node(const T& x)//构造
- :_next(nullptr)
- , _prev(nullptr)
- , _data(x)
- {}
- };
-
- //迭代器最终版
- //const 迭代器 -- 增加模板参数,解决 operator*() 返回值与 operator->() 返回值问题
- //typedef __list_iterator
iterator; - //typedef __list_iterator
const_iterator; - //STL源码中大佬的写法,利用多个模板参数来避免副本造成的代码冗余问题
- template<class T, class Ref, class Ptr>
- struct __list_iterator //迭代器类
- {
- typedef list_node
node; //重命名list节点 - typedef __list_iterator
Self; //这里进行重命名是为了后续再添加模板参数时只用修改这一个地方 - node* _pnode; //节点指针作为类的唯一成员变量
-
- __list_iterator(node* p)
- :_pnode(p)
- {}
-
- Ref operator*() //解引用
- {
- return _pnode->_data;
- }
-
- Ptr operator->() //->
- {
- return &_pnode->_data;
- }
-
- Self& operator++() //前置++
- {
- _pnode = _pnode->_next;
- return *this;
- }
-
- Self& operator++(int) //后置++
- {
- Self it(*this);
- _pnode = _pnode->_next;
- return it;
- }
-
- Self& operator--() //前置--
- {
- _pnode = _pnode->_prev;
- return *this;
- }
-
- Self& operator--(int) //后置--
- {
- Self it(*this);
- _pnode = _pnode->_prev;
- return it;
- }
-
- bool operator!=(const Self& it) const //!=
- {
- return _pnode != it._pnode;
- }
-
- bool operator==(const Self& it) const //==
- {
- return _pnode == it._pnode;
- }
- };
-
- //list 类
- template<class T>
- class list
- {
- typedef list_node
node; //list 的节点 - public:
- typedef __list_iterator
iterator; //迭代器 - typedef __list_iterator
const T&, const T*> const_iterator; //const 迭代器 -
- //迭代器
- iterator begin() {
- return iterator(_head->_next);
- }
-
- iterator end() {
- //iterator it(_head);
- //return it;
-
- //直接利用匿名对象更为便捷
- return iterator(_head);
- }
-
- const_iterator begin() const {
- return const_iterator(_head->_next);
- }
-
- const_iterator end() const {
- return const_iterator(_head);
- }
-
- void empty_initialize() { //初始化 -- 哨兵位头结点
- _head = new node(T());
- _head->_next = _head;
- _head->_prev = _head;
-
- _size = 0; //空间换时间,用于标记节点个数
- }
-
- list() { //构造,不是list
的原因:构造函数函数名和类名相同,而list是类型 - empty_initialize();
- }
-
- //迭代器区间构造
- template <class InputIterator>
- list(InputIterator first, InputIterator last) {
- empty_initialize();
- while (first != last)
- {
- push_back(*first);
- ++first;
- //first++;
- }
- }
-
- //拷贝构造传统写法
- //list(const list
& lt) { - // empty_initialize();
-
- // for (const auto& e : lt)
- // {
- // push_back(e);
- // }
- //}
-
- // 拷贝构造的现代写法
- //list(const list& lt) 官方库是这样写的,这是由于在类内类名等价于类型,但不建议自己这样写
- list(const list
& lt) { - empty_initialize(); //初始化头结点,防止交换后tmp野指针不能正常的调用析构
- list
tmp(lt.begin(), lt.end()) ; - swap(tmp);
- }
-
- //赋值重载传统写法
- //list
& operator=(const list& lt) { - // if (this != <)
- // {
- // clear();
- // for (const auto& e : lt)
- // {
- // push_back(e);
- // }
- // }
- // return *this;
- //}
-
- //赋值重载现代写法
- //list& operator=(list lt)
- list
& operator=(list lt) { //不能加引用,lt是调用拷贝构造生成的 - swap(lt);
- return *this;
- }
-
- ~list() { //析构
- clear();
- delete _head;
- _head = nullptr;
- }
-
- void swap(list
& lt) { //交换两个链表,本质上是交换两个链表的头结点 - std::swap(_head, lt._head);
- std::swap(_size, lt._size);
- }
-
- size_t size() const { //增加一个计数的成员,以空间换时间
- return _size;
- }
-
- bool empty() { //判空
- return _size == 0;
- }
-
- void clear() {
- iterator it = begin();
- while (it != end()) {
- it = erase(it);
- }
- _size = 0;
- }
-
- 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;
-
- insert(end(), x); //复用
- }
-
- void push_front(const T& x) {
- insert(begin(), x); //复用
- }
-
- void pop_front() {
- erase(begin());
- }
-
- void pop_back() {
- erase(--end());
- }
-
- iterator insert(iterator pos, const T& x) {
- node* newnode = new node(x);
- node* cur = pos._pnode;
- node* prev = cur->_prev;
-
- prev->_next = newnode;
- newnode->_prev = prev;
- cur->_prev = newnode;
- newnode->_next = cur;
-
- ++_size;
- return iterator(pos);
- }
-
- iterator erase(iterator pos) {
- assert(pos != end());
-
- node* prev = pos._pnode->_prev;
- node* next = pos._pnode->_next;
-
- prev->_next = next;
- next->_prev = prev;
- delete pos._pnode;
-
- --_size;
- return iterator(next);
- }
-
- private:
- node* _head;
- size_t _size;
- };
- }

完结撒花~