1. 程式人生 > >STL 基本演算法

STL 基本演算法

一.比較區間函式equal

   該函式用於判斷兩個指定區間內的資料是否相等,若第二個區間較長,則多處部分不予考慮,因此,若要保證兩個序列相等則應先判斷其元素個數是否相同。其實現較簡單,沒有過多好說的。

二.填充函式fill,fill_n

1.函式概述 

      fill函式使用指定值填充目標區間,其填充過程是通過呼叫賦值運算子來進行的,fill_n與fill類似,只是其填充的是區間的前n個元素。

2 fill與uninitialized_fill的區別

        uninitialized_fill假設目的區間是未井過初始化的,它會先資料是否未POD型別,若是則直接呼叫fill函式進行填充,否則呼叫複製建構函式在目的區間進行構造,因此若目的區間已初始化則會造成建構函式與解構函式的呼叫次數不匹配。而fill如上所說呼叫的是賦值運算子,因此若操作的區間未經初始化且是非POD;型別資料,則會對未構造物件呼叫賦值運算子,造成未定義的行為。

三.交換所指物件iter_swap

      該函式交換兩個迭代器所指的物件,而非迭代器所指的位置

template <class _ForwardIter1, class _ForwardIter2, class _Tp>
inline void __iter_swap(_ForwardIter1 __a, _ForwardIter2 __b, _Tp*) {
  _Tp __tmp = *__a;
  *__a = *__b;
  *__b = __tmp;
}

template <class _ForwardIter1, class _ForwardIter2>
inline void iter_swap(_ForwardIter1 __a, _ForwardIter2 __b) {
  __iter_swap(__a, __b, __VALUE_TYPE(__a));
}

四.字典序比較lexicographical_compare

       該函式以字典序來比較兩個序列的元素,當序列1小於序列2時(某個元素小於或長度小於)則返回true,否則返回false。用使用者也可以自定義比較方式,而不使用預設的小於操作符。對於c字串(unsigned char*)型別過載了一個函式,通過呼叫memcmp來進行比較。

template <class _InputIter1, class _InputIter2>
bool lexicographical_compare(_InputIter1 __first1, _InputIter1 __last1,
                             _InputIter2 __first2, _InputIter2 __last2) {
  for ( ; __first1 != __last1 && __first2 != __last2
        ; ++__first1, ++__first2) {
    if (*__first1 < *__first2)
      return true;
    if (*__first2 < *__first1)
      return false;
  }
  // 序列1已到末尾,而序列2還未到,即序列1的長度小於序列2的長度
  return __first1 == __last1 && __first2 != __last2;
}


inline bool 
lexicographical_compare(const unsigned char* __first1,
                        const unsigned char* __last1,
                        const unsigned char* __first2,
                        const unsigned char* __last2)
{
  const size_t __len1 = __last1 - __first1;
  const size_t __len2 = __last2 - __first2;
  const int __result = memcmp(__first1, __first2, min(__len1, __len2));
  return __result != 0 ? __result < 0 : __len1 < __len2;
}

五.不匹配點mismatch

      平行的比較兩個序列 [first1, last1),[first2, first2 + last1 - first1) ,返回指向第一個不相等位置的迭代器對(分別指向序列1和序列2,當序列2的長度小於序列1時,會出現為定義行為,因為序列2的長度是根據序列1算的。若相等則都返回last迭代器

六.拷貝函式copy

      copy函式為了提高拷貝效率而採用了過載,萃取,偏特化的程式設計技巧,可謂是無所不用其極。

1.對於指向基礎型別的記憶體區

     實現了接收基礎型別指標的函式過載,直接呼叫memmove來拷貝記憶體,效率高。且其實現方式十分巧妙

#define __SGI_STL_DECLARE_COPY_TRIVIAL(_Tp)                                \
  inline _Tp* copy(const _Tp* __first, const _Tp* __last, _Tp* __result) { \
    memmove(__result, __first, sizeof(_Tp) * (__last - __first));          \
    return __result + (__last - __first);                                  \
  }

__SGI_STL_DECLARE_COPY_TRIVIAL(char)          // 這樣可以免於重新寫函式體
__SGI_STL_DECLARE_COPY_TRIVIAL(signed char)
__SGI_STL_DECLARE_COPY_TRIVIAL(unsigned char)
__SGI_STL_DECLARE_COPY_TRIVIAL(short)
__SGI_STL_DECLARE_COPY_TRIVIAL(unsigned short)
__SGI_STL_DECLARE_COPY_TRIVIAL(int)
__SGI_STL_DECLARE_COPY_TRIVIAL(unsigned int)
__SGI_STL_DECLARE_COPY_TRIVIAL(long)
__SGI_STL_DECLARE_COPY_TRIVIAL(unsigned long)
#ifdef __STL_HAS_WCHAR_T
__SGI_STL_DECLARE_COPY_TRIVIAL(wchar_t)
#endif
#ifdef _STL_LONG_LONG
__SGI_STL_DECLARE_COPY_TRIVIAL(long long)
__SGI_STL_DECLARE_COPY_TRIVIAL(unsigned long long)
#endif 
__SGI_STL_DECLARE_COPY_TRIVIAL(float)
__SGI_STL_DECLARE_COPY_TRIVIAL(double)
__SGI_STL_DECLARE_COPY_TRIVIAL(long double)

#undef __SGI_STL_DECLARE_COPY_TRIVIAL

2.對於只有預設賦值運算子的型別

     在copy呼叫中會通過萃取來在編譯期得知物件是否只有預設的賦值運算子,若是則萃取或得型別struct __true_type{};否則為struct __false_type{};接著通過其在編譯期選擇呼叫哪個函式。程式碼如下:

template <class _InputIter, class _OutputIter>
inline _OutputIter copy(_InputIter __first, _InputIter __last,
                        _OutputIter __result) {
  typedef typename iterator_traits<_InputIter>::value_type _Tp;
  typedef typename __type_traits<_Tp>::has_trivial_assignment_operator _Trivial;//通過是否只有預設賦值運算子來決定型別
  // 通過偏特化來選擇使用哪個仿函式
  return __copy_dispatch<_InputIter, _OutputIter, _Trivial>::copy(__first, __last, __result);
}

// 對於只包含預設賦值運算的型別,呼叫該函式,轉而呼叫memmove函式進行拷貝
template <class _Tp>
struct __copy_dispatch<_Tp*, _Tp*, __true_type>
{
  static _Tp* copy(const _Tp* __first, const _Tp* __last, _Tp* __result) {
    return __copy_trivial(__first, __last, __result);
  }
};

template <class _Tp>
inline _Tp* __copy_trivial(const _Tp* __first, const _Tp* __last, _Tp* __result) {
  memmove(__result, __first, sizeof(_Tp) * (__last - __first));
  return __result + (__last - __first);
}

3.對於過載了賦值運算子的型別

     對於過載了了賦值運算子的型別,會轉而呼叫非偏特化的函式,從而逐元素的呼叫賦值運算子進行拷貝,程式碼如下:

template <class _InputIter, class _OutputIter, class _BoolType>
struct __copy_dispatch {
  static _OutputIter copy(_InputIter __first, _InputIter __last,
                          _OutputIter __result) {
    typedef typename iterator_traits<_InputIter>::iterator_category _Category;
    typedef typename iterator_traits<_InputIter>::difference_type _Distance;
    return __copy(__first, __last, __result, _Category(), (_Distance*) 0);
  }
};

    再判斷迭代器型別,從而選擇最快的方式進行遍歷,對於隨機迭代器呼叫以下程式碼:

template <class _RandomAccessIter, class _OutputIter, class _Distance>
inline _OutputIter
__copy(_RandomAccessIter __first, _RandomAccessIter __last,
       _OutputIter __result, random_access_iterator_tag, _Distance*)
{
  for (_Distance __n = __last - __first; __n > 0; --__n) { // 以n決定迴圈次數,速度快
    *__result = *__first;
    ++__first;
    ++__result;
  }
  return __result;
}

    而其它迭代器呼叫以下程式碼:

template <class _InputIter, class _OutputIter, class _Distance>
inline _OutputIter __copy(_InputIter __first, _InputIter __last,
                          _OutputIter __result,
                          input_iterator_tag, _Distance*)
{
  for ( ; __first != __last; ++__result, ++__first) // 以迭代器是否相等判斷,速度慢
    *__result = *__first;
  return __result;
}