1. 程式人生 > >STL容器-deque-雙端佇列

STL容器-deque-雙端佇列

註明:全部來自轉載,供自己學習與複習使用

 

deque雙向開口可進可出的容器

我們知道連續記憶體的容器不能隨意擴充,因為這樣容易擴充別人那去

deque卻可以,它創造了記憶體連續的假象.

其實deque由一段一段構成 ,他是分段連續,而不是記憶體連續 當走向段的尾端時候自動跳到下一段 所以支援迭代器++ 操作,自動跳到下一段的方法由operator++實現

deque每次擴充 申請一個段

一 定義

標頭檔案 #include <deque>

int main_0() {

    //預設建構函式 建立一個空的deque
    deque<int> c;
    
    //拷貝構造
    deque<int> c1(c);
    
    //賦值拷貝
    deque<int> c2 = c1;
    
    //指定元素個數建立
    deque<int> c3 (5,6);
    for (auto i : c3) {
        cout<< i << ",";
    }
    cout << "deque(個數, 元素)"<<endl;
    
    //指定區間建立
    deque<int> c4(c3.begin()+2, c3.begin()+3);
    for (auto i : c4) {
        cout<< i << ",";
    }
    cout << "deque(區間, 區間)"<<endl;
    
    //指定初始化列表建立
    deque<int> c5({2,3,4,5});
    for (auto i : c5) {
        cout<< i << ",";
    }
    cout << "deque({})"<<endl;
    
    //指定初始化列表建立
    deque<int> c6 = {2,3,4,5};
    for (auto i : c6) {
        cout<< i << ",";
    }
    cout << "deque = {}" <<endl;
    cout<<endl;
    return 0;
}

 

二 操作

 

int main()
{
    deque<int> c = {1,2,3,4,5};
    deque<int> c1;
    
    // 賦值初始化
    c1.assign(c.begin(),c.end());
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "assign()" <<endl;

    //在尾部插入
    c1.push_back(6);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "push_back()" <<endl;
    
    //頭插入
    c1.push_front(0);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "push_front()" <<endl;
    
    //彈尾元素
    c1.pop_back();
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "pop_back()" <<endl;
    
    //彈頭元素
    c1.pop_front();
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "pop_front()" <<endl;
    
    //指定位置插入元素
    c1.insert(c1.begin()+3, 10);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "insert()" <<endl;
    
    //刪除指定位置元素
    c1.erase(c1.begin()+3);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "erase()" <<endl;

    //清空deque
    c.clear();
    for (auto i: c) {
        cout<< i << ",";
    }
    cout << "clear()" <<endl;
    
    //構造
    c1.emplace(c1.end(), 100);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "emplace()" <<endl;
    
    //頭位置插入元素
    c1.emplace_front(111);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "emplace_front()" <<endl;
    
    //尾位置插入元素
    c1.emplace_back(111);
    for (auto i: c1) {
        cout<< i << ",";
    }
    cout << "emplace_back()" <<endl;
    
    //交換
    c.swap(c1);
    for (auto i: c) {
        cout<< i << ",";
    }
    cout << "swap()" <<endl;
    
    int tmp;
    tmp = c.front();
    cout<<"第一個元素"<< tmp << endl;
    
    tmp = c.back();
    cout<<"最後一個元素"<< tmp << endl;
    
    tmp = c.at(1);
    cout<<"指定下標元素"<< tmp << endl;
    
    tmp = c[1];
    cout<<"指定[]下標元素"<< tmp << endl;

    return 0;
}

 


 

三 記憶體

int main()
{
    deque<int> c = {1,2,3,4,5};
    
    //元素個數
    cout<< "size(): " << c.size() <<endl;
    
    //重新設定deque大小 少退多補
    c.resize(10, 5);
    for(auto i : c)
    {
        cout << i <<",";
    }
    cout << "resize()" << endl;
    
    //最大容量
    cout << "max_size(): " << c.max_size() <<endl;
    
    //是否為空
    cout << "empty: " << c.empty() << endl;
    
    //清空記憶體
    c.shrink_to_fit();
    
    return 0;
}

 


 

五 與迭代器操作

int main()
{
    deque<int> c = {1,2,3,4,5};
    //指向第一個元素的迭代器
    cout << *c.begin() << endl;
    //指向最後一個元素的下一個位置的迭代器
    cout << *c.end() << endl;
    //反向deque的第一個元素的迭代器
    cout << *c.rbegin() << endl;
    //反向deque的第一個元素的上一個位置迭代器
    cout << *c.rend() << endl;
    //指向第一個元素的迭代器
    cout << *c.cbegin() << endl;
    //指向最後一個元素的下一個位置的迭代器
    cout << *c.cend() << endl;
    //反向deque的第一個元素的迭代器
    cout << *c.crbegin() << endl;
    //反向deque的第一個元素的上一個位置迭代器
    cout << *c.crend() << endl;
    
    return 0;
}

 


Soource Code

 deque的類圖如下,跟vector的類圖結構相似

_Deque_base<_Tp>基類

    template<typename _Tp, typename _Alloc>
    class _Deque_base
    {
        ...
        struct _Deque_impl
        : public _Tp_alloc_type
        {
            _Tp** _M_map;
            size_t _M_map_size;
            iterator _M_start;
            iterator _M_finish;
        }
        ...
    }

_Deque_impl<_Tp>資料類

成員變數含義,如下圖所示 

_M_map指向儲存  指向記憶體的指標  的連續記憶體

_M_map_size表示_M_map指向的記憶體大小(有多少個buffer)

_M_start指向_M_map指向記憶體起始點的迭代器

_M_finish指向_M_map指向記憶體結束點的迭代器

deque<_Tp>

    
    template<typename _Tp, typename _Alloc = std::allocator<_Tp> >
    class deque : protected _Deque_base<_Tp, _Alloc>
    {
        //成員變數含義與vector成員變數含義相似
    public:
        typedef _Tp                                        value_type;
        typedef typename _Tp_alloc_type::pointer           pointer;
        typedef typename _Tp_alloc_type::const_pointer     const_pointer;
        typedef typename _Tp_alloc_type::reference         reference;
        typedef typename _Tp_alloc_type::const_reference   const_reference;
        typedef typename _Base::iterator                   iterator;
        typedef typename _Base::const_iterator             const_iterator;
        typedef std::reverse_iterator<const_iterator>      const_reverse_iterator;
        typedef std::reverse_iterator<iterator>            reverse_iterator;
        typedef size_t                             size_type;
        typedef ptrdiff_t                          difference_type;
        typedef _Alloc                             allocator_type;
        
        ...
        //這個函式返回buffer大小
        static size_t _S_buffer_size() _GLIBCXX_NOEXCEPT
        { return __deque_buf_size(sizeof(_Tp)); }
        
    }
    
    //它的實現如下
#ifndef _GLIBCXX_DEQUE_BUF_SIZE
#define _GLIBCXX_DEQUE_BUF_SIZE 512
#endif
    
    inline size_t
    __deque_buf_size(size_t __size)
    { return (__size < _GLIBCXX_DEQUE_BUF_SIZE
        ? size_t(_GLIBCXX_DEQUE_BUF_SIZE / __size) : size_t(1)); }
    /*
     如果是deque<int>的話 上面的函式可以翻譯成 4 < 512 ? size_t(512/4) " size_t(1)     
     結果就是512/4 = 128
     那麼每個buffer所含value_type的個數就128,即一個buffer能存128個int
     
     deque<double> 的話就是512/8 = 64 那麼每個buffer的所含個數就是64
     
     如果>=512 就是每個buffer只能存1個 (class/array/struct)
    */

 

解析一個成員函式insert(),在解析之前介紹一下它的迭代器成員變數的含義

start 指向頭的迭代器(_M_start)

finish 指向尾部的下一個位置的迭代器(_M_finish)

cur 指向當前元素

first 指向當前buffer內的第一個元素

last 指向單錢buffer的最後一個元素

node 指向當前buffer的指標

map_size node的個數

     insert(const_iterator __position, value_type&& __x)
     { return emplace(__position, std::move(__x)); }

 

empalce()

     template<typename _Tp, typename _Alloc>
     template<typename... _Args>
     typename deque<_Tp, _Alloc>::iterator
     deque<_Tp, _Alloc>::
     emplace(const_iterator __position, _Args&&... __args)
     {
     if (__position._M_cur == this->_M_impl._M_start._M_cur) //插入的位置是否在最前端
     {
     emplace_front(std::forward<_Args>(__args)...);
     return this->_M_impl._M_start;
     }
     else if (__position._M_cur == this->_M_impl._M_finish._M_cur)//插入的位置在最尾端
     {
     emplace_back(std::forward<_Args>(__args)...);
     iterator __tmp = this->_M_impl._M_finish;
     --__tmp;//迭代器指向的是最尾端的下一個位置
     return __tmp;
     }
     else
     return _M_insert_aux(__position._M_const_cast(),
     std::forward<_Args>(__args)...);
     }

 

 emplace_front()

 template<typename _Tp, typename _Alloc>
     template<typename... _Args>
     void
     deque<_Tp, _Alloc>::
     emplace_front(_Args&&... __args)//插入在最前端
     {
     if (this->_M_impl._M_start._M_cur != this->_M_impl._M_start._M_first)//如果最前端的buffer記憶體夠的話
     {
     this->_M_impl.construct(this->_M_impl._M_start._M_cur - 1,
     std::forward<_Args>(__args)...);//直接在備用空間上構造元素
     --this->_M_impl._M_start._M_cur;//調整cur
     }
     else //如過記憶體不夠
     _M_push_front_aux(std::forward<_Args>(__args)...);
     }
     
     
     #if __cplusplus >= 201103L
     template<typename... _Args>
     void
     deque<_Tp, _Alloc>::
     _M_push_front_aux(_Args&&... __args)
     #else
     void
     deque<_Tp, _Alloc>::
     _M_push_front_aux(const value_type& __t)
     #endif
     {
     _M_reserve_map_at_front();
     *(this->_M_impl._M_start._M_node - 1) = this->_M_allocate_node();//申請空的頭結點
     __try
     {
     this->_M_impl._M_start._M_set_node(this->_M_impl._M_start._M_node
     - 1); //改變start,令其指向新節點
     this->_M_impl._M_start._M_cur = this->_M_impl._M_start._M_last - 1; ////改變cur,
     #if __cplusplus >= 201103L
     this->_M_impl.construct(this->_M_impl._M_start._M_cur,
     std::forward<_Args>(__args)...);//複製
     #else
     this->_M_impl.construct(this->_M_impl._M_start._M_cur, __t);
     #endif
     }
     __catch(...)
     {
     ++this->_M_impl._M_start;
     _M_deallocate_node(*(this->_M_impl._M_start._M_node - 1));
     __throw_exception_again;
     }
     }

_M_insert_aux

#if __cplusplus >= 201103L
     template<typename... _Args>
     typename deque<_Tp, _Alloc>::iterator
     deque<_Tp, _Alloc>::
     _M_insert_aux(iterator __pos, _Args&&... __args)
     {
     value_type __x_copy(std::forward<_Args>(__args)...); // XXX copy
     #else
     typename deque<_Tp, _Alloc>::iterator
     deque<_Tp, _Alloc>::
     _M_insert_aux(iterator __pos, const value_type& __x)
     {
     value_type __x_copy = __x; // XXX copy
     #endif
     difference_type __index = __pos - this->_M_impl._M_start;//重在operator- 返回距起始端距離 安插點之前的元素個數
     if (static_cast<size_type>(__index) < size() / 2)//若果插入點在整體記憶體的前半段,插入後整體記憶體前移
     {
     push_front(_GLIBCXX_MOVE(front()));//複製頭buffer的一個元素
     iterator __front1 = this->_M_impl._M_start;//取出頭迭代器位置,和下一個節點的起始點
     ++__front1;
     iterator __front2 = __front1;
     ++__front2;
     __pos = this->_M_impl._M_start + __index;
     iterator __pos1 = __pos;
     ++__pos1;
     _GLIBCXX_MOVE3(__front2, __pos1, __front1);//front1 到 front2 這段區間內的前pos1個元素向前移動一個單位 (留出一個單位給將插入的的元素)
     }
     else
     {
     push_back(_GLIBCXX_MOVE(back()));
     iterator __back1 = this->_M_impl._M_finish;
     --__back1;
     iterator __back2 = __back1;
     --__back2;
     __pos = this->_M_impl._M_start + __index;
     _GLIBCXX_MOVE_BACKWARD3(__pos, __back2, __back1);
     }
     *__pos = _GLIBCXX_MOVE(__x_copy); //複製元素
     return __pos;
     }

 

既然說deque是模仿記憶體連續,實現這種功能的主要功臣是deque的迭代器,說道迭代器 那麼我們一定會聯想到迭代器的操作符過載,下面我主要介紹迭代器的操作符過載

operator* 這個比較好理解 返回cur指向的元素

        reference
        operator*() const _GLIBCXX_NOEXCEPT
        { return *_M_cur; }
        
        pointer
        operator->() const _GLIBCXX_NOEXCEPT
        { return _M_cur; }

 

 

operator++()(前++)與operator++(int)

        _Self&
        operator++() _GLIBCXX_NOEXCEPT
        {
            ++_M_cur;
            if (_M_cur == _M_last)//如果++到達該節點尾端
      {
          _M_set_node(_M_node + 1);//跳至下一個節點
          _M_cur = _M_first;
      }
            return *this;
        }
        
        _Self
        operator++(int) _GLIBCXX_NOEXCEPT
        {
            _Self __tmp = *this;
            ++*this;//呼叫前++
            return __tmp;
        }

 

 

 

operator--()與operator--(int)

        _Self&
        operator--() _GLIBCXX_NOEXCEPT
        {
            if (_M_cur == _M_first)//如果--後到達頭端
      {
          _M_set_node(_M_node - 1);//跳至前一個節點
          _M_cur = _M_last;
      }
            --_M_cur;
            return *this;
        }
        
        _Self
        operator--(int) _GLIBCXX_NOEXCEPT
        {
            _Self __tmp = *this;
            --*this;
            return __tmp;
        }

 

 

operator+=(itn) ,+,-=,-

    
        _Self&
        operator+=(difference_type __n) _GLIBCXX_NOEXCEPT
        {
            const difference_type __offset = __n + (_M_cur - _M_first);//查詢位置
            if (__offset >= 0 && __offset < difference_type(_S_buffer_size()))//查詢位置是否小於一個buffer的容量(在當前buffer內)
                _M_cur += __n;
            else
      {
          const difference_type __node_offset =
          __offset > 0 ? __offset / difference_type(_S_buffer_size())
          : -difference_type((-__offset - 1)
                             / _S_buffer_size()) - 1;
          _M_set_node(_M_node + __node_offset);//切換至正確buffer
          _M_cur = _M_first + (__offset - __node_offset
                               * difference_type(_S_buffer_size()));
      }
            return *this;
        }
        
        _Self
        operator+(difference_type __n) const _GLIBCXX_NOEXCEPT
        {
            _Self __tmp = *this;
            return __tmp += __n;//呼叫+=
        }
        
        _Self&
        operator-=(difference_type __n) _GLIBCXX_NOEXCEPT
        { return *this += -__n; }//呼叫+=
        
        _Self
        operator-(difference_type __n) const _GLIBCXX_NOEXCEPT
        {
            _Self __tmp = *this;
            return __tmp -= __n;
        }

 

operator[],和_M_set_node

        
        reference
        operator[](difference_type __n) const _GLIBCXX_NOEXCEPT
        { return *(*this + __n); }
        
        /**
         *  Prepares to traverse new_node.  Sets everything except
         *  _M_cur, which should therefore be set by the caller
         *  immediately afterwards, based on _M_first and _M_last.
         */
        void
        _M_set_node(_Map_pointer __new_node) _GLIBCXX_NOEXCEPT//切換至正確buffer
        {
            _M_node = __new_node;
            _M_first = *__new_node;
            _M_last = _M_first + difference_type(_S_buffer_size());
        }

 

原文出處:

http://www.cnblogs.com/LearningTheLoad/p/7450948.html