目录
🌈前言🌈
📁 介绍
📁 使用
📂 构造
📂 迭代器iterator
📂 capacity
📂 modifiers
📂 迭代器失效
📁 模拟实现
📂 迭代器的实现
📂 代码展示
📁 和vector的区别
📁 总结
🌈前言🌈
欢迎收看本期【C++杂货铺】,本期内容将讲解STL中关于list的内容,会分为一下几个方面进行讲解:第一,什么是list,和其他容器的比较;第二,list的常用接口的介绍;第三,从底层除法,模拟实现简易版list;最后,对比和vecotr的主要区别。
以上就是本期要讲解的全部内容了,讲解之前需要你有数据结构中链表的储备知识,是为了更好的理解讲解内容。此外,模拟实现需要类和对象,模板等储备知识,如果只是想简单使用可以只看前两章。
如果你还没有类和对象及模板的知识,可以阅览下面这几篇文章:【C++杂货铺】详解类和对象 [上]-CSDN博客
【C++杂货铺】详解类和对象 [中]-CSDN博客
【C++杂货铺】详解类和对象 [下]-CSDN博客
【C++杂货铺】模板-CSDN博客
📁 介绍
list是可以在常熟范围内在任意位置进行插入和删除的序列式容器,并且该容器可以前后双向迭代。
list底层是双向链表结构,双向链表中每个元素存储在互不关联的独立节点中,在节点通过指针指向其前一个元素和后一个元素。
和其他的序列式容器相比(vector,array,deque),list通常在任意位置进行插入,移动元素的执行效率更好。
与之相对的,list的最大缺陷是不支持在任意位置的随机访问。
📁 使用
list接口较多,这里简单介绍一些常用的重要接口。
下面是官方文档的链接,对于容器的学习还是要多看文档的。
list - C++ Reference (cplusplus.com)
📂 构造
📂 迭代器iterator
📂 capacity
📂 modifiers
📂 迭代器失效
迭代器失效即迭代器所指向的节点无效,即该节点被删除。因为list底层是带头结点的双向循环链表,因此在list中进行插入时是不会导致迭代器失效的,只有在删除时才会失效,并且失效只是指向所删除结点的迭代器,其他迭代器不会受到影响。
void TestListIterator1() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list l(array, array+sizeof(array)/sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { // erase()函数执行后,it所指向的节点已被删除,因此it无效,在下一次使用it时,必须先给 其赋值 l.erase(it); ++it; } } // 改正 void TestListIterator() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list l(array, array+sizeof(array)/sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { l.erase(it++); // it = l.erase(it); } }
📁 模拟实现
对于list的模拟实现最难实现的就是迭代器的实现。所以下面将重点讲解迭代器内容。
📂 迭代器的实现
迭代器就是通过模拟指针,提供一种统一的方法来使用容器。
对于vecotr这样的容器,迭代器可以直接是指针,但对于list这样的容器,不能直接使用原生指针,因为底层内存地址不是连续的。
指针++,并不能实现node = node->next这样的操作。这里就要用到C++的重要组成部分了,运算符重载和实现了类。通过将指针封装成类,来实现运算符重载。
这里迭代器使用三个模板参数,第一个表示数据data的类型,第二表示数据data的引用,第三个表示数据data的地址。
template struct ListIterator { typedef ListNode Node; typedef ListIterator iterator; //构造函数 ListIterator(Node* node) :_node(node) {} Ref operator*() { return _node->data; } Ptr operator->() { return &_node->data; } //++it iterator& operator++() { _node = _node->_next; return *this; } iterator operator++(int) { iterator temp(*this); _node = _node->_next; return temp; } //--it iterator& operator--() { _node = _node->_prev; return *this; } iterator operator--(int) { iterator temp(*this); _node = _node->_prev; return temp; } // != bool operator!=(const iterator& it) { return _node != it._node; } bool operator==(const iterator & it) { return _node == it._node; } Node* _node; };
📂 代码展示
下面将list的实现,放在exercise命名空间中。
namespace exercise { //节点类 template struct ListNode { typedef ListNode Node; Node* _next; Node* _prev; T data; ListNode(const T& val = T()) :_next(nullptr) ,_prev(nullptr) ,data(val) {} }; template struct ListIterator { typedef ListNode Node; typedef ListIterator iterator; //构造函数 ListIterator(Node* node) :_node(node) {} Ref operator*() { return _node->data; } Ptr operator->() { return &_node->data; } //++it iterator& operator++() { _node = _node->_next; return *this; } iterator operator++(int) { iterator temp(*this); _node = _node->_next; return temp; } //--it iterator& operator--() { _node = _node->_prev; return *this; } iterator operator--(int) { iterator temp(*this); _node = _node->_prev; return temp; } // != bool operator!=(const iterator& it) { return _node != it._node; } bool operator==(const iterator & it) { return _node == it._node; } Node* _node; }; //list类 template class list { typedef ListNode Node; public: typedef ListIterator iterator; typedef ListIterator const_iterator; void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; } list() { empty_init(); } list(const list& l) { empty_init(); for (auto& e : l) { push_back(e); } } void swap(list& temp) { std::swap(_head,temp._head); std::swap(_size, temp._size); } list& operator=(list temp) { swap(temp); return *this; } void clear() { iterator it = _head->_next; while (it != _head) { it = erase(it); } } ~list() { clear(); delete _head; _head = nullptr; _size = 0; } iterator begin() { return _head->_next; } iterator end() { return _head; } const_iterator begin() const { return _head->_next; } const_iterator end() const { return _head; } /*void push_back(const T& val) { Node* newnode = new Node(val); Node* tail = _head->_prev; newnode->_prev = tail; newnode->_next = _head; _head->_prev = newnode; tail->_next = newnode; }*/ void push_back(const T& val) { insert(end(), val); } void insert(iterator pos, const T& val) { Node* newnode = new Node(val); Node* prev = pos._node->_prev; Node* next = pos._node; newnode->_prev = prev; prev->_next = newnode; newnode->_next = next; next->_prev = newnode; _size++; } iterator erase(iterator pos) { Node* prev = pos._node->_prev; Node* next = pos._node->_next; delete pos._node; prev->_next = next; next->_prev = prev; _size--; return next; } void pop_back() { erase(--end()); } size_t size() { return _size; } private: Node* _head; size_t _size; }; }
📁 和vector的区别
📁 总结
以上,就是本期【C++杂货铺】的主要内容了,包含了list的介绍,list常用接口以及模拟实现list。
如果感觉本期内容有帮助到你,欢迎点赞,收藏,关注Thanks♪(・ω・)ノ