1. 程式人生 > >STL容器之size()函式的實現

STL容器之size()函式的實現

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原始碼剖析》吧,分析對比一下,也有一些樂趣。