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