初识C++|list类的使用及模拟实现

🍬 mooridy-CSDN博客

🧁C++专栏(更新中!)

目录

list的介绍

list的使用

list的构造

list 容量

list 访问

list 增删查改

迭代器

迭代器失效问题

list模拟实现

list与vector的对比

emplace_back和push_back的区别


list的介绍

list底层——带头双向循环链表

list的使用

list的构造

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

list<int> lt1;                                // 构造空列表
list<int> lt2 (4,100);                       // 构造有4个100的列表
list<int> lt3 (lt2.begin(),lt2.end());  // 用迭代器构造
list<int> lt4 (third);                       // 拷贝构造

list 容量

函数声明接口说明
empty检测list是否为空,是返回true,否则返回false
size返回list中有效节点的个数

list 访问

函数声明接口说明
front返回list的第一个节点中值的引用
back返回list的最后一个节点中值的引用

list 增删查改

函数声明接口说明
push_frontlist首元素前插入值为val的元素
pop_front删除list中第一个元素
push_backlist尾部插入值为val的元素
pop_back删除list中最后一个元素
insertlist position 位置前插入值为val的元素
erase删除list position位置的元素
swap交换两个list中的元素
clear清空list中的有效元素
list<int> first (3,100);   
list<int> second (5,200);  
 it = first.begin();
first.insert(it,5);//在it前加1个5
first.insert(it,2,6);//在it前加2个6
  first.swap(second);

迭代器

1.

之前我们学习vector容器时可以简单的将迭代器理解为指针。然而在list中,这有的理解显然是有失偏颇的。如list的介绍中所提到的,list的底层实现是带头双向循环链表,而我们知道链表的空间是不连续的,如果还以为迭代器是指针,那么++的位置只能是结点的下一个位置,但这个理解显然不对。

那又为什么我们却能像vector一样的去使用list的迭代器呢?
而且*就是访问的数据,++和- -就能找到结点的后一个结点和前一个结点。

我们能用类似vector迭代器的方式操作list迭代器,是因为C++的类封装和操作符重载。迭代器封装了复杂的容器遍历逻辑,通过重载++--*等操作符,让我们能以直观方式访问元素,而无需关心其底层实现细节。

具体是如何实现的,可以看看下面的list模拟实现部分。

2.

在使用过程中,需注意list的迭代器只有++,--的功能,一步步的移动迭代器,是无法像vector的迭代器一样实现+或-功能进行跨越的加减。

迭代器按性质划分:

性质代表容器功能
单向ForwardIteratorforward_lisr/unordered_map...++
双向BidirectionalIteratorlist/map/set...++/--
随机RandomAcessIteratorvector/string/deque++/--/+/-

底层结构可以决定使用哪些算法。

迭代器失效问题

迭代器失效即迭代器所指向的节点的无效,即该节点被删除了。因为list的底层结构为带头结点的双向循环链表,因此在list中进行插入时是不会导致list的迭代器失效的,只有在删除时才会失效,并且失效的只是指向被删除节点的迭代器,其他迭代器不会受到影响。

insert不会失效——只是修改了指针指向关系,而没有修改迭代器本身

erase——指向删除元素的迭代器失效

list模拟实现

//list.h
#include<iostream>
using namespace std;


namespace AA
{
    // List的节点类
    template<class T>
    struct ListNode
    {
        ListNode(const T& val = T()){
            _val = val;
            _prev = nullptr;
            _next=nullptr;
        }
        ListNode<T>* _prev;
        ListNode<T>* _next;
        T _val;
    };


    //List的迭代器类
    template<class T,class Ref, class Ptr>
    class ListIterator
    {
        typedef ListNode<T> Node;
        typedef ListIterator<T,Ref,Ptr> Self;
    public:
        ListIterator(Node* pNode = nullptr) {
            _pNode = pNode;
        }
        ListIterator(const Self& l) {
            *this = l;
        }
        Ref operator*() {
            return _pNode->_val;
        }
        Ptr operator->() {
            return &_pNode->_val;
        }
        Self& operator++() {
            _pNode = _pNode->_next;
            return *this;
        }
        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& l) {
            return _pNode != l._pNode;
        }
        bool operator==(const Self& l) {
            return _pNode == l._pNode;
        }
    private:
        Node* _pNode;
    };


    //list类
    template<class T>
    class list
    {
        typedef ListNode<T> Node;
        //typedef Node* PNode;
    public:
        typedef ListIterator<T, T&, T*> iterator;
        typedef ListIterator<T, const T&, const T&> const_iterator;
    public:
        ///
        // List的构造
        list() {
            _pHead = new Node;
            _pHead->next = _pHead;
            _pHead->prev = _pHead;
            _size = 0;
        }
        list(int n, const T& value = T()) {
            _pHead = new Node;
            Node* pcur = _pHead;
            for (int i = 0; i < n; i++) {
                Node* newnode = new Node;
                newnode->val = value;
                newnode->next = _pHead;
                newnode->prev = pcur;
                pcur->next = newnode;
                pcur = newnode;
                _pHead->prev = pcur;
          
            }
            _size = n;
        }
        template <class Iterator>
        list(Iterator first, Iterator last);
        list(const list<T>& l);
        list<T>& operator=(const list<T> l);
        ~list() {
            clear();
            delete _pHead;
            _pHead = nullptr;
            _size = 0;
        }


        ///
        // List Iterator
        iterator begin() {
            return _pHead->next;
        }
        iterator end() {
            return _pHead->prev;
        }
        const_iterator begin() {
            return _pHead->next;
        }
        const_iterator end() {
            return _pHead->prev;
        }


        ///
        // List Capacity
        size_t size()const {
            return _size;
        }
        bool empty()const {
            return size == 0;
        }


        
        // List Access
        T& front() {
            return _pHead->next->_val;
        }
        const T& front()const {
            return _pHead->next->_val;
        }
        T& back() {
            return _pHead->prev->_val;
        }
        const T& back()const {
            return _pHead->prev->_val;
        }


        
        // List Modify
        void push_back(const T& val) { insert(end(), val); }
        void pop_back() { erase(--end()); }
        void push_front(const T& val) { insert(begin(), val); }
        void pop_front() { erase(begin()); }
        // 在pos位置前插入值为val的节点
        iterator insert(iterator pos, const T& val) {
            Node* newnode = new Node;
            newnode->_val = val;
            Node* pcur = pos._pNode;
            Node* prev = pcur->_prev;
            newnode->_next = pcur;
            newnode->_prev = prev;
            prev->_next = newnode;
            pcur->_prev = newnode;
            ++_size;
            return newnode;
        }
        // 删除pos位置的节点,返回该节点的下一个位置
        iterator erase(iterator pos) {
            Node* pcur = pos._pNode;
            Node* prev = pcur->_prev;
            Node* next = pcur->_next;
            delete pcur;
            pcur = nullptr;
            prev->_next = next;
            next->_prev = prev;

            --_size;
            return next;
        }
        void clear() {
            Node* pcur = _pHead->next;
            while (pcur!=_pHead) {
                pcur = erase(pcur);
            }
        }
        void swap(list<T>& l) {
            std::swap(_pHead, l._pHead);
            std::swap(_size, l._size);
        }
    private:
        size_t _size;
        Node* _pHead;
    };
};

list与vector的对比

vectorlist
底层结构动态顺序表,一段连续空间
带头结点的双向循环链表
随机访问
支持随机访问,访问某个元素效率 O(1)
不支持随机访问,访问某个元 素效率 O(N)
插入和删除
任意位置插入和删除效率低,需要搬移元素,时间 复杂度为 O(N) ,插入时有可能需要增容,增容需要 开辟新空间,拷贝元素,释放旧空间,导致效率更
任意位置插入和删除效率高,
不需要搬移元素,时间复杂度
O(1)
空间利用率
底层为连续空间,不容易造成内存碎片,空间利用 率高,缓存利用率高
底层节点动态开辟,小节点容 易造成内存碎片,空间利用率 低,缓存利用率低
迭代器
原生态指针
对原生态指针 ( 节点指针 ) 进行 封装
迭代器失效
在插入元素时,要给所有的迭代器重新赋值,因为 插入元素有可能会导致重新扩容,致使原来迭代器 失效,删除时,当前迭代器需要重新赋值否则会失
插入元素不会导致迭代器失 效,删除元素时,只会导致当 前迭代器失效,其他迭代器不 受影响
应用场景
需要高效存储,支持随机访问,不关心插入删除效
大量插入和删除操作,不关心 随机访问

emplace_back和push_back的区别

push_back() 向容器尾部添加元素时,首先会创建这个元素,然后再将这个元素拷贝或者移动到容器中(如果是拷贝的话,事后会自行销毁先前创建的这个元素);

而 emplace_back() 在实现时,则是直接在容器尾部创建这个元素,省去了拷贝或移动元素的过程。这样效率更高。

同时在使用上,有下图代码所展示的小区别:

请注意:emplace_back() 是 C++ 11 标准新增加的,如果程序需要兼顾之前的版本,还是应该使用 push_back()。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值