日本免费高清视频-国产福利视频导航-黄色在线播放国产-天天操天天操天天操天天操|www.shdianci.com

學(xué)無先后,達(dá)者為師

網(wǎng)站首頁 編程語言 正文

C++ STL - list 模擬實(shí)現(xiàn)+解析迭代器

作者:CPP的底層是哲學(xué) 更新時(shí)間: 2022-10-14 編程語言

目錄

list基本介紹和使用

list模擬實(shí)現(xiàn)

list的迭代器:

理解:

const_iterator問題:

list迭代器失效問題:

list的反向迭代器

理解:?

reverse_iterator.h

反向迭代器的operator*實(shí)現(xiàn):

operator->()?

vector和list的比較


list基本介紹和使用

list - 帶頭雙向循環(huán)鏈表。

對比vector,因?yàn)閘ist是由一個(gè)個(gè)結(jié)點(diǎn)連接而成的,致使list
1. 不支持元素的隨機(jī)訪問,即沒有operator[],因?yàn)樾实汀TL問元素front back
2. 沒有capacity的概念,內(nèi)存允許的情況下,可以無限申請節(jié)點(diǎn)并擴(kuò)展
3. 支持push_front pop_front,使得list可以作為queue的適配容器
4. 最大的優(yōu)勢是在任意位置插入刪除都可以在O(1)時(shí)間完成

學(xué)習(xí)成員函數(shù) 見https://cplusplus.com/reference/list/list/

list模擬實(shí)現(xiàn)

#ifndef STL_LIST_H
#define STL_LIST_H

#include <cstddef>   // size_t
#include <cassert>
#include <iostream>
#include "reverse_iterator.h"
//using namespace std;
namespace yzl
{
    template<class T>
    struct list_node  // list內(nèi)部使用的結(jié)點(diǎn)類型
    {
    public:
        T _data;
        list_node<T>* _next;
        list_node<T>* _prev;
    public:
        list_node(const T& data = T())  // 結(jié)點(diǎn)的默認(rèn)構(gòu)造函數(shù)
        : _data(data), _next(nullptr), _prev(nullptr)
        { }
//        ~list_node() = default;
//        // 拷貝構(gòu)造,其實(shí)沒必要
//        list_node(const list_node<T>& node)
//        : _data(node._data), _next(node._next), _prev(node._prev)  // 淺拷貝
//        { }
//        // 賦值運(yùn)算符重載,也沒必要
//        list_node<T>& operator=(const list_node<T>& node)  // 淺拷貝
//        {
//            _data = node._data;
//            _prev = node._prev;
//            _next = node._next;
//            return *this;
//        }
    };

    // 說真的,只是對結(jié)點(diǎn)指針的一個(gè)封裝罷了,構(gòu)造函數(shù)也是通過一個(gè)結(jié)點(diǎn)指針即可構(gòu)造出一個(gè)迭代器。
    // 對于迭代器的操作,其實(shí)也就是++ -- 然后解引用。目前的操作就是這些,之后再有再補(bǔ)充吧。
    template<class T, class Ref, class Ptr> // T&  T*  or  const T& const T*
    class __list_iterator  // 成員就是一個(gè)結(jié)點(diǎn)指針
    {
    public:
        typedef list_node<T> Node;  // 這是一個(gè)結(jié)構(gòu)體,結(jié)點(diǎn)。Node就是一個(gè)結(jié)點(diǎn)結(jié)構(gòu)體。
        typedef __list_iterator<T, Ref, Ptr> iterator;

        typedef std::bidirectional_iterator_tag iterator_category;
        typedef T value_type;
        typedef Ptr pointer;
        typedef Ref reference;
        typedef ptrdiff_t difference_type;
    public:
        Node* _node;  // 這個(gè)迭代器類型有一個(gè)數(shù)據(jù)成員,是結(jié)點(diǎn)指針。
    public:
        __list_iterator(Node* node)
        :_node(node)
        { }
        // 這個(gè)類型的實(shí)例化對象本身不支持解引用操作,operator*賦予了它解引用之后的操作。
        Ref operator*()
        {
            return _node->_data;
        }
        Ptr operator->()
        {
            return &(operator*());
//            return &(_node->_data);
        }
        bool operator!=(const iterator& it)
        {
            return _node != it._node;
        }
        bool operator==(const iterator& it)
        {
            return _node == it._node;
        }
        // 思考一下迭代器++,普通迭代器和const迭代器其實(shí)都可以++
        iterator& operator++() // 前置
        {
            _node = _node->_next;
            return *this;
        }
        iterator operator++(int)  // 后置
        {
            iterator tmp = *this;
            this->_node = this->_node->_next;
            return tmp;
        }

        iterator& operator--()
        {
            _node = _node->_prev;
            return *this;
        }
        iterator operator--(int)
        {
            iterator tmp(*this);
            this->_node = this->_node->_prev;
            return tmp;
        }
    };

    // list<int> ls;
    template<class T>
    class list
    {
    private:
        typedef list_node<T> Node;
        Node* _head;    // list_node<int>* _head;

    public:
        // 說真的,關(guān)于list的迭代器,你只需要實(shí)現(xiàn)begin end這一系列的即可,還要讓它的返回值支持++ 解引用操作,這就夠了,說真的。
        typedef __list_iterator<T, T&, T*> iterator;   // 這個(gè)結(jié)構(gòu)體別名為迭代器,本身是不支持解引用操作的。
        //typedef const __list_iterator<T> const_iterator;  // 你這里const修飾的是迭代器類型,他有結(jié)點(diǎn)指針數(shù)據(jù)成員,const使得結(jié)點(diǎn)指針不能改變。
        // 但是根本上,const迭代器是指不能修改*迭代器所指的值。而不是迭代器本身。
        typedef __list_iterator<T, const T&, const T*> const_iterator;

//        typedef __list_reverse_iterator<T, T&, T*> reverse_iterator;
//        typedef __list_reverse_iterator<T, const T&, const T*> const_reverse_iterator;
        typedef __reverse_iterator<__list_iterator<T, T&, T*>, T&, T*> reverse_iterator;
        typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;
        iterator begin()
        {
            return iterator(_head->_next);
        }
        iterator end()
        {
            return iterator(_head);
        }
        const_iterator begin() const
        {
            return const_iterator(_head->_next);
        }
        const_iterator end() const
        {
            return const_iterator(_head);
        }
        const_iterator cbegin() const
        {
            return const_iterator(_head->_next);
        }
        const_iterator cend() const
        {
            return const_iterator(_head);
        }
        reverse_iterator rbegin()
        {
            return reverse_iterator(end());
        }
        reverse_iterator rend()
        {
            return reverse_iterator(begin());
        }
        const_reverse_iterator rbegin() const
        {
            return const_reverse_iterator(end());
        }
        const_reverse_iterator rend() const
        {
            return const_reverse_iterator(begin());
        }
        const_reverse_iterator crbegin() const
        {
            return const_reverse_iterator(end());
        }
        const_reverse_iterator crend() const
        {
            return const_reverse_iterator(begin());
        }
    public:
        list() {   // list的默認(rèn)構(gòu)造函數(shù)
            _head = new Node;   // 調(diào)用list_node<T>的默認(rèn)構(gòu)造函數(shù),頭節(jié)點(diǎn)里面的data是隨機(jī)數(shù)
            _head->_next = _head;
            _head->_prev = _head;
        }
        ~list()
        {
            clear();
            delete _head;
            _head = nullptr;
        }
        void empty_init()
        {
            _head = new Node;
            _head->_next = _head;
            _head->_prev = _head;
        }
        template<class InputIterator>
        list(InputIterator first, InputIterator last)
        {
            empty_init();
            while(first != last)
            {
                push_back(*first);
                ++first;
            }
        }
        // 拷貝構(gòu)造函數(shù)現(xiàn)代寫法
        list(const list<T>& ls)
        {
            empty_init();
            list<T> tmp(ls.begin(), ls.cend());
            this->swap(tmp);
        }
//        list(const list<T>& ls)
//        :_head(new Node)
//        {
//            _head->_next = _head;
//            _head->_prev = _head;
//            for(auto&i:ls)
//            {
//                Node* newnode = new Node(i);
//                Node* tail = _head->_prev;
//                tail->_next = newnode;
//                newnode->_prev = tail;
//                newnode->_next = _head;
//                _head->_prev = newnode;
//            }
//        }
        // ls1 = ls2
        list<T>& operator=(list<T> ls)
        {
            this->swap(ls);
            return *this;
        }
    public:
        void push_back(const T& val)
        {
            Node* newnode = new Node(val);
            Node* tail = _head->_prev;
            tail->_next = newnode;
            newnode->_prev = tail;
            newnode->_next = _head;
            _head->_prev = newnode;
        }
        void push_front(const T& val)
        {
            Node* newnode = new Node(val);
            Node* first = _head->_next;
            _head->_next = newnode;
            newnode->_prev = _head;
            newnode->_next = first;
            first->_prev = newnode;
        }
        iterator insert (iterator position, const T& val)
        {
            Node* cur = position._node;
            Node* prev = cur->_prev;
            Node* newnode = new Node(val);

            prev->_next = newnode;
            newnode->_prev = prev;
            newnode->_next = cur;
            cur->_prev = newnode;

            return iterator(newnode);
        }

        iterator erase(iterator position)
        {
            assert(position._node != _head);

            Node* cur = position._node;
            Node* prev = cur->_prev;
            Node* next = cur->_next;

            prev->_next = next;
            next->_prev = prev;
            delete cur;

            return iterator(next);
        }
        void pop_back()
        {
            assert(size() > 0);
            Node* tail = _head->_prev;
            Node* newtail = tail->_prev;
            newtail->_next = _head;
            _head->_prev = newtail;
            delete tail;
        }
        void pop_front()
        {
            assert(size() > 0);
            Node* front = _head->_next;
            Node* newfront = _head->_next->_next;
            _head->_next = newfront;
            newfront->_prev = _head;
            delete front;
        }
        void show() const
        {
            Node* tmp = _head->_next;
            while(tmp != _head)
            {
                std::cout<<tmp->_data<<" ";
                tmp = tmp->_next;
            }
            std::cout << std::endl;
        }
        void swap(list<T>& ls)
        {
            std::swap(_head, ls._head);
        }
        size_t size() const
        {
            size_t ret = 0;
            Node* tmp = _head->_next;
            while(tmp != _head)
            {
                ++ret;
                tmp = tmp->_next;
            }
            return ret;
        }
        bool empty() const
        {
            return _head->_next == _head;
        }
        T& front()
        {
            assert(size() > 0);
            return _head->_next->_data;
        }
        const T& front() const
        {
            assert(size() > 0);
            return _head->_next->_data;
        }
        T& back()
        {
            assert(size() > 0);
            return _head->_prev->_data;
        }
        const T& back() const
        {
            assert(size() > 0);
            return _head->_prev->_data;
        }
        void clear()  // const不能使用
        {
//            auto it = begin();
//            while(it != end())
//            {
//                it = it = erase(it);
//            }
            Node* it = cbegin()._node;
            while(it != cend()._node)
            {
                Node* prev = it->_prev;
                Node* next = it->_next;
                prev->_next = next;
                next->_prev = prev;
                delete it;
                it = next;
            }
        }
    };
}


#endif //STL_LIST_H

list_node 是結(jié)點(diǎn)類,存儲data next prev ,很清楚。
而list,只有一個(gè)數(shù)據(jù)成員,就是一個(gè)頭節(jié)點(diǎn)指針? list_node<T>* _head;

先不談list的迭代器。push_back, pop_back, push_front pop_front size front back等都是帶頭雙向循環(huán)鏈表的基本套路。

list的迭代器:

理解:

我們知道,vector的迭代器是原生指針,這是因?yàn)関ector底層存儲的緣故,原生指針完全符合迭代器的要求。
那么,list是一個(gè)雙向帶頭循環(huán)鏈表,由一個(gè)個(gè)結(jié)點(diǎn)組成,結(jié)點(diǎn)指針顯然不能滿足迭代器的要求, 因?yàn)榻庖煤笫墙Y(jié)點(diǎn)實(shí)例化對象list_node<T>,而不是數(shù)據(jù)data。結(jié)點(diǎn)指針++也不是下一個(gè)結(jié)點(diǎn),因?yàn)榻Y(jié)點(diǎn)的存儲不連續(xù)。
不過,基于C++的特性:封裝和運(yùn)算符重載,我們可以把結(jié)點(diǎn)指針進(jìn)行封裝,再使用運(yùn)算符重載重新定義其解引用和++的操作,使其符合迭代器的要求。于是,有了__list_iterator

    template<class T, class Ref, class Ptr> // T&  T*  or  const T& const T*
    class __list_iterator  // 成員就是一個(gè)結(jié)點(diǎn)指針
    {
    public:
        typedef list_node<T> Node;  // 這是一個(gè)結(jié)構(gòu)體,結(jié)點(diǎn)。Node就是一個(gè)結(jié)點(diǎn)結(jié)構(gòu)體。
        typedef __list_iterator<T, Ref, Ptr> iterator;

    public:
        Node* _node;  // 這個(gè)迭代器類型有一個(gè)數(shù)據(jù)成員,是結(jié)點(diǎn)指針。
    public:
        __list_iterator(Node* node)
        :_node(node)
        { }
        // 這個(gè)類型的實(shí)例化對象本身不支持解引用操作,operator*賦予了它解引用之后的操作。
        Ref operator*()
        {
            return _node->_data;
        }
        Ptr operator->()
        {
            return &(operator*());
//            return &(_node->_data);
        }
        bool operator!=(const iterator& it)
        {
            return _node != it._node;
        }
        bool operator==(const iterator& it)
        {
            return _node == it._node;
        }
        // 思考一下迭代器++,普通迭代器和const迭代器其實(shí)都可以++
        iterator& operator++() // 前置
        {
            _node = _node->_next;
            return *this;
        }
        iterator operator++(int)  // 后置
        {
            iterator tmp = *this;
            this->_node = this->_node->_next;
            return tmp;
        }

        iterator& operator--()
        {
            _node = _node->_prev;
            return *this;
        }
        iterator operator--(int)
        {
            iterator tmp(*this);
            this->_node = this->_node->_prev;
            return tmp;
        }
    };

其實(shí)非常簡單,這個(gè)迭代器類型就是對結(jié)點(diǎn)指針的一個(gè)封裝。和list一樣,它只有一個(gè)數(shù)據(jù)成員,即Node*? ?(typedef?list_node<T> Node)?

然后進(jìn)行一些運(yùn)算符重載,比如operator* operator++ operator==,這里也是C++的基礎(chǔ)知識,因?yàn)榻Y(jié)點(diǎn)指針不能直接做迭代器,所以將其封裝,但是封裝好的類__list_iterator對象本身并不支持解引用 ++ 操作,C++的運(yùn)算符重載特性使得可以定義這個(gè)類對象的某些操作。? 之后就可以使其像迭代器一樣使用,即__list_iterator的對象是list的一個(gè)迭代器。

        typedef __list_iterator<T, T&, T*> iterator;   // 這個(gè)結(jié)構(gòu)體別名為迭代器,本身是不支持解引用操作的。
        typedef __list_iterator<T, const T&, const T*> const_iterator;

在list內(nèi)進(jìn)行如上typedef即可。?

const_iterator問題:

        //typedef const __list_iterator<T> const_iterator;  // 你這里const修飾的是迭代器類型,他有結(jié)點(diǎn)指針數(shù)據(jù)成員,const使得結(jié)點(diǎn)指針不能改變。

如何區(qū)分list的iterator 和 const_iterator是一個(gè)問題,首先我們需要明確const_iterator需要什么特殊性質(zhì):解引用后的值是const的,且是引用性質(zhì)的。迭代器本身可以++ --。如上代碼是最初我想的一個(gè)錯(cuò)誤的實(shí)現(xiàn),挺愚蠢的。const_iterator 并不是 const __list_iterator,這里的const __list_iterator使得這個(gè)迭代器存儲的Node*不能改變,也就是迭代器不能++ --。這是錯(cuò)誤的。

根本上我們需要控制的是迭代器__list_iterator類對象operator*? operator->的返回值,普通迭代器返回普通引用,const迭代器返回const &,這里通過模板參數(shù)即可實(shí)現(xiàn)。

通過在list中實(shí)例化時(shí)模板參數(shù)不同,從而確定operator*的返回值。

    template<class T, class Ref, class Ptr> // T&  T*  or  const T& const T*
    class __list_iterator  // 成員就是一個(gè)結(jié)點(diǎn)指針

        typedef __list_iterator<T, T&, T*> iterator;   // 這個(gè)結(jié)構(gòu)體別名為迭代器,本身是不支持解引用操作的。
        typedef __list_iterator<T, const T&, const T*> const_iterator;

基于模板的知識我們知道,模板的參數(shù)不同,則實(shí)際上是完全不同的兩個(gè)類。上方的iterator 和 const_iterator大部分都是一樣的,只有operator* 和 operator->的返回值不同,他們都可以完成operator++ opeator--操作。

list迭代器失效問題:

迭代器失效即迭代器所指向的節(jié)點(diǎn)的無效,即該節(jié) 點(diǎn)被刪除了。因?yàn)閘ist的底層結(jié)構(gòu)為帶頭結(jié)點(diǎn)的雙向循環(huán)鏈表,因此在list中進(jìn)行插入時(shí)是不會導(dǎo)致list的迭代 器失效的,只有在刪除時(shí)才會失效,并且失效的只是指向被刪除節(jié)點(diǎn)的迭代器,其他迭代器不會受到影響。

void TestListIterator1()
{
 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()函數(shù)執(zhí)行后,it所指向的節(jié)點(diǎn)已被刪除,因此it無效,在下一次使用it時(shí),必須先給
其賦值
 l.erase(it); 
 ++it;
 }
}
// 改正
void TestListIterator()
{
 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())
 {
 l.erase(it++); // it = l.erase(it);
 }
}

list的反向迭代器

STL的反向迭代器實(shí)際上運(yùn)用的是適配器設(shè)計(jì)模式。

理解:?

首先,還是要明白反向迭代器的操作,再來說如何實(shí)現(xiàn)它更方便。
反向迭代器,無非就是++是往前,--是往后。同樣支持解引用操作 和 == != 判斷。

那么,感覺正向和反向迭代器的操作并沒有太大變化,無非++ -- 的方向不同。故我們可以基于正向迭代器的操作,進(jìn)行封裝,然后設(shè)計(jì)出反向迭代器。? (這里在學(xué)完stack queue priority_queue之后再學(xué)會更好理解一點(diǎn))

reverse_iterator.h

namespace yzl
{
    template <class Iterator, class Ref, class Ptr>  // 沒必要傳T
    class __reverse_iterator
    {
        // 反向迭代器
    public:
        typedef __reverse_iterator<Iterator, Ref, Ptr> reverse_iterator;
    public:
        __reverse_iterator(const Iterator& _it)  // 用一個(gè)正向迭代器去構(gòu)造反向迭代器
        :it(_it)
        {}
        Ref operator*() // 反向迭代器的解引用操作和正向的一樣
        {
//            Iterator tmp = --it;
//            ++it;
//            return *tmp;
            auto tmp = it;
            --tmp;
            return *tmp;
        }
        Ptr operator->()
        {
            return &(this->operator*());
        }
        reverse_iterator& operator++()  // 前置++,迭代器向后一步即可。
        {
            --it;   // 調(diào)用構(gòu)造函數(shù)中傳來的迭代器的operator--()
            return *this;
        }
        reverse_iterator operator++(int)
        {
            reverse_iterator ret = *this;
            --it;
            return ret;
        }
        reverse_iterator& operator--()
        {
            ++it;
            return *this;
        }
        reverse_iterator operator--(int)
        {
            auto ret = *this;
            ++it;
            return ret;
        }
        bool operator==(const reverse_iterator& rit)
        {
            return it == rit.it;
        }
        bool operator!=(const reverse_iterator& rit)
        {
            return it != rit.it;
        }
    private:
        Iterator it;
    };
}

模板參數(shù)Iterator就是傳來的正向迭代器,只有一個(gè)數(shù)據(jù)成員,即一個(gè)正向迭代器it,因?yàn)檎虻饕呀?jīng)支持了* -> ++ -- == !=操作,所以上述實(shí)現(xiàn)都是基于這個(gè)基礎(chǔ)來完成的。

因?yàn)槲覀儍H憑傳來的一個(gè)正向迭代器無法直接得出其保存的元素類型,即T的類型(不考慮類型萃取)。所以增加了兩個(gè)類型參數(shù)Ref Ptr,用于指明operator* operator->的返回值

這個(gè)不僅適用于list,vector也同樣適用。因?yàn)関ector的正向迭代器(原生指針)支持那些操作。所以直接利用適配器設(shè)計(jì)模式進(jìn)行封裝即可得到反向迭代器。

        typedef __reverse_iterator<__list_iterator<T, T&, T*>, T&, T*> reverse_iterator;
        typedef __reverse_iterator<const_iterator, const T&, const T*> const_reverse_iterator;

在list內(nèi)進(jìn)行如上typedef并非必要操作,而是這樣在下面rbegin rend處更方便...

反向迭代器的operator*實(shí)現(xiàn):

        Ref operator*() // 反向迭代器的解引用操作和正向的一樣
        {
//            Iterator tmp = --it;
//            ++it;
//            return *tmp;
            auto tmp = it;
            --tmp;
            return *tmp;
        }

這里的operator*并不是直接獲取此正向迭代器所指元素,而是其前一個(gè)元素。這樣做不是必須的。

如上圖,這樣做僅僅為了讓begin 和 rend對應(yīng)上,end和rbegin對應(yīng)上。
如果operator*是直接返回正向迭代器所值元素,則rbegin指向7,rend指向1前面的那個(gè)位置。
同理,在list中,則rbegin()指向最后一個(gè)元素,rend指向頭節(jié)點(diǎn)。

operator->()?

我們知道,迭代器是一個(gè)類似指針的東西,->運(yùn)算符一般是用于自定義類型對象指針使用->直接獲取其某個(gè)數(shù)據(jù)成員。比如Date* p = &d1;? ?cout << p->year << endl; 那么迭代器重載->是為什么呢?

迭代器指向元素類型不確定,比如int,或者一個(gè)Date。這要看容器存儲的什么元素類型。迭代器重載->就是為了當(dāng)?shù)髦赶蜃远x類型時(shí)使用->可以直接獲取其數(shù)據(jù)成員比如:

std::list<Date> ls;
ls.push_back(Date(2001,1,1));
ls.push_back(Date(2002,1,1));
std::list<date>::iterator it = ls.begin();
cout << it->year << endl;

這里應(yīng)該輸出2001,這里it->year就調(diào)用了迭代器的operator-> 返回值是元素指針即Date*。
那么 it->的返回值是一個(gè)Date*,又是如何直接和year連接起來呢?實(shí)際上這里省略了一個(gè)->應(yīng)該是it->->year;

void test11()
{
    yzl::list<Date> ls;
    ls.push_back(Date(2001,1,1));
    ls.push_back(Date(2002,2,2));
    yzl::list<Date>::reverse_iterator rit = ls.rbegin();
    
    cout<<rit.operator->()->year<<endl;
    cout<<rit->year<<endl;
}

結(jié)果2002

vector和list的比較

?偷的

原文鏈接:https://blog.csdn.net/i777777777777777/article/details/127283890

欄目分類
最近更新