STL容器之size()函式的實現
阿新 • • 發佈:2019-01-22
vector O(1)
list O(n)
deque
以下轉自:http://www.07net01.com/2014/09/79439.html
使用C++進行開發,一部分人可能喜歡使用STL,即C++標準模板庫。對容器使用可能最多,演算法相對比較少。我在開發中基本上也就是使用容器:vector, list, deque, map, hashtable, hash_map。
在實際情況不是特別要求自己需要實現特殊的資料結構的時候,一般採用STL容器,是又快又好又安全的做法。
孫子兵法曰:“知己知彼,百戰不殆”。
我們一般人寫程式,不可避免要使用別人開發的庫來搭積木。其實也講究這個“知己知彼”:
知己—-對自己程式要實現的業務邏輯分析清楚,時間上、 空間上的複雜度等;
知彼—-對我們所使用的庫也要有一定的瞭解,瞭解的越清楚越好;當然我們可能不會去完全瞭解各個庫,這要看個人的修養方向了。
回到STL容器來說,一般容器都有size()成員函式的實現,我們很明白它返回的是容器裡元素的個數,一般可能都認為這個size()應該及其簡單,時間複雜度應該是O(1)。實際情況是不是這樣呢,我們簡單分析下vector, list, deque(我下載的是SGI原始碼)。
先看vector:
以下是程式碼片段: class vector { public: size_type size() const { return size_type(end() – begin()); } iterator begin() { return _M_start; } iterator end() { return _M_finish; } protected: _Tp* _M_start; _Tp* _M_finish; _Tp* _M_end_of_storage; }; |
可見,vector的size()實現在時間複雜度上確實是常量級的,很簡單很快,就如我們想像中一樣。注意它沒有一個_size。
再看list:
以下是程式碼片段: class list{ public: size_type size() const { size_type __result = 0; distance(begin(), end(), __result); return __result; } }; |
注意了,盡然不是我們想象的。既不同於vector實現,也沒有一個_size, 而是呼叫的一個全域性函式:
標頭檔案stl_iterator_base.h:
以下是程式碼片段: template <class _InputIterator, class _Distance> inline void distance(_InputIterator __first, _InputIterator __last, _Distance& __n) { __STL_REQUIRES(_InputIterator, _InputIterator); __distance(__first, __last, __n, iterator_category(__first)); } template <class _InputIterator, class _Distance> inline void __distance(_InputIterator __first, _InputIterator __last, _Distance& __n, input_iterator_tag) { while (__first != __last) { ++__first; ++__n; } } |
大家看到了吧,它竟然使用迭代器遍歷了整個連結串列,那麼size()在時間複雜度上就不再是我們想象中的常量級,而是O(n)。為什麼這樣呢,這要對iterator和traits有所瞭解,才明白這麼做的必要性。
所以,如果你的list裡資料很多,而你每次操作前還要呼叫size()來看看是否超過你的最大數目,那執行速度比你想象中要慢很多。所以處理這種情況,如果你必須每次要看看它的元素數目,把list包裝一下,自己維護一個_size算了,也浪費不了多少記憶體,4位元組嘛。
最後看看deque:
以下是程式碼片段: class deque { public: size_type size() const { return _M_finish – _M_start; } protected: iterator _M_start; iterator _M_finish; }; |
不錯,看起來和vector一樣,其實不然。
別忘了c++語法有一個重要概念:運算子過載。
以下是程式碼片段: struct _Deque_iterator { typedef _Deque_iterator<_Tp, _Tp&, _Tp*> iterator; typedef ptrdiff_t difference_type; typedef _Deque_iterator _Self; difference_type operator-(const _Self& __x) const { return difference_type(_S_buffer_size()) * (_M_node – __x._M_node – 1) + (_M_cur – _M_first) + (__x._M_last – __x._M_cur); } }; |
為什麼這麼複雜了?這要怪deque採用map作為主控,使用分段連續線性空間的設計。怎麼說都比list那個實現快。
可以說STL的程式碼風格是讓人難受的,當我們無聊而心情又好的時候,讀讀侯捷的《STL原始碼剖析》吧,分析對比一下,也有一些樂趣。