C++中的ilst使用以及模拟实现
作者:派小星233
list是一个类模板,加<类型>实例化才是具体的类,可以在任意位置进行插入和删除的序列式容器,本文将通过代码示例给大家介绍一下C++中的ilst使用以及模拟实现,需要的朋友可以参考下
list介绍
- list是一个类模板,加<类型>实例化才是具体的类。
- list是可以在任意位置进行插入和删除的序列式容器。
- list的底层是双向循环链表结构,链表中每个元素存储在互不相关的独立节点中,在节点中通过指针指向其前一个元素和后一个元素。
- 与其他序列式容器相比,list最大的缺陷是不支持任意位置的随机访问,比如访问第六个节点,必须从已知节点迭代到该节点。
双向链表图解:
list常用接口
1.构造
函数 | 功能 |
---|---|
list (size_type n, const value_type& val = value_type()) | 构造的list中包含n个值为val的节点 |
list() | 构造空的list |
list (const list& x) | 拷贝构造 |
list (InputIterator first, InputIterator last) | 迭代器区间初始化 (模板,可以传入其它容器的迭代器区间) |
2.迭代器
函数 | 功能 |
---|---|
begin()加end() | 获取第一个数据位置的iterator/const_iterator,获取最后一个数据的下一个位置的iterator/const_iterator |
rbegin()加rend() | 反向迭代器,获取最后一个数据位置的reverse_iterator,获取第一个数据前一个位置的reverse_iterator |
3.容量
函数 | 功能 |
---|---|
size() | 获取有效数据个数 |
empty() | 判断是否为空(size为0为空,返回true) |
4.访问数据
函数 | 功能 |
---|---|
front() | 取到头节点数据的引用 |
back() | 返回尾节点数据的引用 |
5.增删查改
函数 | 功能 |
---|---|
push_front (const value_type& val) | 头插数据val |
push_back (const value_type& val) | 尾删数据val |
pop_front() | 头删 |
pop_back() | 尾删 |
insert (iterator position, const value_type& val) | 在position位置中插入值为val的元素 |
erase (iterator position) | 删除position位置的元素 |
swap(list& x) | 交换两个list |
clear() | 清空有效数据 |
6.迭代器失效
迭代失效即迭代器所指向的节点的无效,即该节点被删除了。 因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。
#include<iostream> #include<list> using namespace std; //错误代码演示 int main() { int array[] = { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; list<int> l(array, array + sizeof(array) / sizeof(array[0])); auto it = l.begin(); while (it != l.end()) { // erase()函数执行后,it所指向的节点已被删除 //因此it无效,在下一次使用it时,必须先给其赋值 it = l.erase(it); //只需要去掉++it,这里修改成it = erase(it)即可 ++it; } return 0; }
list模拟实现
1.迭代器的实现
有别于vector,list的迭代器并不是一个原生的指针,因为使用者需要得到的是节点中的数据而不是整个节点,而且寻找下一个节点并不能通过指针简单的++,所以需要把迭代器单独封装成一个类,通过重载*等运算符来完成需求。
namespace MyList { //节点设计成结构体,方便访问 template<typename T> struct list_node { list_node(const T val = T()) :_data(val) , _next(nullptr) , _prev(nullptr) {} T _data; list_node<T>* _next; list_node<T>* _prev; }; //迭代器 //这里设计模板参数除了迭代器,还有Ref(引用)和Ptr(指针) //这样设计是为了同时生成普通迭代器和const对象的迭代器 //普通对象(可读可写):iterator<T, T&, T*> //const对象(可读不可写):const_iterator<T, const T&, const T*> template<typename T, typename Ref, typename Ptr> struct __list_iterator { typedef list_node<T> Node; typedef __list_iterator<T, Ref, Ptr> self; //要返回迭代器需要返回实例化对象,重命名一下 Node* _node; __list_iterator(Node* p) :_node(p) {} 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->_prev; return tmp; } Ref operator*() { return _node->_data; } //返回指针可以让自定义类型自行打印,访问成员 //->操作符,比较特殊,it->_num转换出来其实是it.operator->()->_num Ptr operator->() { return &(_node->_data); } bool operator!=(const self& s) { return _node != s._node; } bool operator==(const self& s) { return _node == s._node; } }; //反向迭代器 //反向迭代器需要进行封装,其实就是复用普通迭代器,然后++和--操作反过来 //普通对象(可读可写):Reverse_iterator<iterator,T&,T*> //const对象(可读不可写):Reverse_iterator<const_iterator,const T&,const T*> template<class Iterator, class Ref, class Ptr> struct Reverse_iterator { typedef Reverse_iterator<Iterator, Ref, Ptr> self; Iterator _it; //构造 Reverse_iterator(Iterator it) :_it(it) {} self& operator++() { _it--; return *this; } self operator++(int) { self tmp(*this); _it--; return tmp; } self& operator--() { _it++; return *this; } self operator--(int) { self tmp(*this); _it++; return tmp; } Ref operator*() { return *_it; } Ptr operator->() { return _it; } bool operator!=(const self& s) { return _it != s._it; } bool operator==(const self& s) { return _it == s._it; } }; }
2.完整代码
#pragma once #include<iostream> using namespace std; namespace MyList { //节点设计成结构体,方便访问 template<typename T> struct list_node { list_node(const T val = T()) :_data(val) , _next(nullptr) , _prev(nullptr) {} T _data; list_node<T>* _next; list_node<T>* _prev; }; //迭代器 //这里设计模板参数除了迭代器,还有Ref(引用)和Ptr(指针) //这样设计是为了同时生成普通迭代器和const对象的迭代器 //普通对象(可读可写):iterator<T, T&, T*> //const对象(可读不可写):const_iterator<T, const T&, const T*> template<typename T, typename Ref, typename Ptr> struct __list_iterator { typedef list_node<T> Node; typedef __list_iterator<T, Ref, Ptr> self; //要返回迭代器需要返回实例化对象,重命名一下 Node* _node; __list_iterator(Node* p) :_node(p) {} 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->_prev; return tmp; } Ref operator*() { return _node->_data; } //返回指针可以让自定义类型自行打印,访问成员 //->操作符,比较特殊,it->_num转换出来其实是it.operator->()->_num Ptr operator->() { return &(_node->_data); } bool operator!=(const self& s) { return _node != s._node; } bool operator==(const self& s) { return _node == s._node; } }; //反向迭代器 //反向迭代器需要进行封装,其实就是复用普通迭代器,然后++和--操作反过来 //普通对象(可读可写):Reverse_iterator<iterator,T&,T*> //const对象(可读不可写):Reverse_iterator<const_iterator,const T&,const T*> template<class Iterator, class Ref, class Ptr> struct Reverse_iterator { typedef Reverse_iterator<Iterator, Ref, Ptr> self; Iterator _it; Reverse_iterator(Iterator it) :_it(it) {} self& operator++() { _it--; return *this; } self operator++(int) { self tmp(*this); _it--; return tmp; } self& operator--() { _it++; return *this; } self operator--(int) { self tmp(*this); _it++; return tmp; } Ref operator*() { return *_it; } Ptr operator->() { return _it; } bool operator!=(const self& s) { return _it != s._it; } bool operator==(const self& s) { return _it == s._it; } }; template<typename T> class list { typedef list_node<T> Node; public: typedef __list_iterator<T, T&, T*> iterator; typedef __list_iterator<T, const T&, const T*> const_iterator; typedef Reverse_iterator<iterator, T&, T*> reverse_iterator; typedef Reverse_iterator< const_iterator, const T&, const T*> reverse_const_iterator; //迭代器部分 iterator begin() { return _head->_next; } iterator end() { return _head; } const_iterator begin()const { return _head->_next; } const_iterator end()const { return _head; } reverse_iterator rbegin() { return (--end());//_head->_prev } reverse_iterator rend() { return (end());//_head } reverse_const_iterator rbegin()const { return (--end());//_head->_prev } reverse_const_iterator rend()const { return (end());//_head } /// // /// private: //不希望外界调用,设计成私有 void empty_init() { _head = new Node; _head->_next = _head; _head->_prev = _head; _size = 0; } public: //构造、析构部分 list() { empty_init(); } list(size_t n, const T& value = T()) { empty_init(); while (n--) { push_back(value); } } //重载给内置类型使用,整形默认是int,不写这个会优先匹配list(Iterator first, Iterator last) list(int n, const T& value = T()) { empty_init(); while (n--) { push_back(value); } } //迭代器区间初始化 template <class Iterator> list(Iterator first, Iterator last) { empty_init(); while(first != last) { push_back(*first); first++; } } list(const list<T>& lt) { empty_init(); for (auto e : lt) { push_back(e); } } ~list() { clear(); delete _head; _head = nullptr; } //其它 void swap(list<T> lt) { std::swap(_size, lt._size); std::swap(_head, lt._head); } //使用传之传参,直接拷贝一份交换操作的底层空间就好 list<T>& operator=(list<T> lt) { swap(lt); return *this; } void clear() { iterator it = begin(); while (it != end()) { it = erase(it); } } /// / / //访问头,尾数据 T& front() { return _head->_next->_data; } const T& front()const { return _head->_next->_data; } T& back() { return _head->_prev->_data; } const T& back()const { return _head->_prev->_data; } /// // /// //增加删除部分 void push_back(const T& val) { insert(end(), val); } void push_front(const T& val) { insert(begin(), val); } iterator insert(iterator pos, const T& val) { Node* newnode = new Node(val); Node* cur = pos._node; Node* prev = cur->_prev; cur->_prev = newnode; newnode->_next = cur; newnode->_prev = prev; prev->_next = newnode; ++_size; return newnode; } void pop_back() { erase(--end()); } void pop_front() { erase(begin()); } iterator erase(iterator pos) { Node* cur = pos._node; Node* prev = cur->_prev; Node* next = cur->_next; prev->_next = next; next->_prev = prev; delete cur; --_size; return next; } //获取有效数据量 size_t size() { return _size; } private: Node* _head; //这里存储卫兵节点,因为底层是双向循环链表,可以找到头和尾 size_t _size; //只需要在insert和erase里面加减就可以 }; }
以上就是C++中的ilst使用以及模拟实现的详细内容,更多关于C++ list使用及实现的资料请关注脚本之家其它相关文章!