本编介绍STL容器之List~1、迭代器的功能性质2、辨别模板中应使用哪种迭代器3、list接口的介绍和使用4、模板的按需实例化5、List中迭代器失效的问题6、C11统一初始化7、默认构造、拷贝构造、赋值运算符重载、析构函数的简便实现写法1、迭代器的功能性质区分迭代器可从功能与性质两方面区分。功能iteratorreverse_iteratorconst_iteratorconst_reverse_iterator性质单向forward_list/unordered_map...支持双向list/map/set...支持/--随机vector/string/deque...支持/--//-迭代器性质的不同其支持的运算就不同这种差异源自于底层结构的区别。对于这三个性质可以将其归类随机迭代器可以看作是特殊的双向迭代器特殊的单向迭代器双向迭代器可以看作是特殊的单向迭代器他们之间可以特殊化的归为其一类~2、辨别模板中应使用哪种迭代器随机迭代器Random Access双向迭代器Bidirectional单向迭代器Forward包含这三个的~Input/Output在模板中看到 Random Access说明可用迭代器范围只能用随机迭代器看到 Bidirectional说明可用迭代器范围能用 随机/双向 迭代器看到 Forward说明可用迭代器范围能用 随机/双向/单向 迭代器看到 Input/Output说明可用迭代器范围能用 随机/双向/单向 迭代器。3、list接口的介绍和使用此处只介绍与vector有差异的接口~emplace_back对于单参数的尾插push_back和emplace_back是一样的都要构造再拷贝但是emplace_back是支持多参数的对于多参数为一个元素的尾插emplace_back就直接构造了。resize在链表中的 resize 就没有扩容的概念了。splice有两个list将一个list里面的一个数或一段数据剪切掉再粘贴到另一个list上。unique去重但是是针对于有序的数据。merge合并两个链表被拿去合并的链表合并后变成空链表合并成的链表保持升序。sort对于小数据list自带的 sort 快对于大数据algorithm里面的 sort 快。4、模板的按需实例化模拟实现List中的具体例子// 模板 可以实例化出下面两个 -- 正常版/const版 template class T, class Ref, class Ptr struct list_iterator { typedef list_nodeT Node; typedef list_iteratorT, Ref, Ptr Self; Node* _node; list_iterator(Node* node) :_node(node) { } Ref operator* () { return _node-_data; } Ptr operator- () // 强制语法规定返回指针 { return _node-_data; } Self operator () { _node _node-_next; return *this; } Self operator--() { _node _node-_prev; return *this; } bool operator! (const Self s) const { return _node ! s._node; } bool operator (const Self s) const { return _node s._node; } };该模板在没有具体需求时编译器不会细致扫描就像你知道你记得你有某样东西一样。当有具体需求时模板根据具体需求产生对应的实例化类供具体需求使用。5、List中迭代器失效的问题List在物理空间上是不连续的而且迭代器是经过努力转化出来的指向哪里就指向哪里所以插入时迭代器不失效。测试代码 void test_list2() { listint lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_back(4); listint::iterator it lt.begin(); // 插入迭代器不会失效 lt.insert(it, 10); *it 100; listint::iterator it1 lt.begin(); while (it1 ! lt.end()) { cout *it1 ; it1; } cout endl; }List在删除结点时当前迭代器就像是野指针一样所以List删除时迭代器是失效的。测试代码 void test_list2() { listint lt; lt.push_back(1); lt.push_back(2); lt.push_back(3); lt.push_back(4); // 删除所有偶数 // 迭代器失效 listint::iterator it lt.begin(); while (it ! lt.end()) { if (*it % 2 0) { lt.erase(it); } else { it; } } listint::iterator it1 lt.begin(); while (it1 ! lt.end()) { cout *it1 ; it1; } cout endl; }6、C11统一初始化C11之后支持了这样的初始化的方式// 直接构造 listint lt0({ 1,2,3,4,5,6 }); // 隐式类型转换 listint lt1 { 1,2,3,4,5,6,7,8 }; const listint lt3 { 1,2,3,4,5,6,7,8 }; func(lt0); func({ 1,2,3,4,5,6 });其中隐式类型转换大括号一串的数字会转换成 initializer_listint 类型initializer_list初始化列表。auto il { 10, 20, 30 }; initializer_listint il { 10, 20, 30 }; cout typeid(il).name() endl; cout sizeof(il) endl;实现方式是前后两个指针仅作了解。7、默认构造、拷贝构造、赋值运算符重载、析构函数的简便实现写法void empty_init() { _head new Node; _head-_next _head; _head-_prev _head; _size 0; } // 默认构造 list() { empty_init(); } // l2(l1) 拷贝构造 list(const listT lt) { empty_init(); for (auto e : lt) { push_back(e); } } // C11统一初始化的隐式类型转换 list(initializer_listT lt) { empty_init(); for (auto e : lt) { push_back(e); } } // l2 l1 赋值运算符重载 listint operator (listT lt) { swap(lt); } void swap(listT lt) { std::swap(_head, lt._head); std::swap(_size, lt._size); } // 析构 ~list() { clear(); delete _head; _head nullptr; } void clear() { auto it begin(); while (it ! end()) { it earse(it); } }