Algorithm Library Design 演算法庫設計in c++ II(STL與泛型程式設計)

來源:互聯網
上載者:User
3.STL and Generic Programming
  • 簡介
STL的編程模式是泛型程式設計,有以下幾個核心思想:
  • 對於演算法而言儘可能少的假設資料類型,反之也是如此,從而使得演算法和資料能夠儘可能好的彼此協作.

          Expressing algorithms with minimal assumptions about data abstractions, and vice versa,

          thus making them as interoperable as possible.

  • 儘可能的將演算法向上泛化而同時不丟失運行效率.即泛化的演算法當特化之後其效率與原始的演算法是一樣的.

           Lifting of a concrete algorithm to as general a level as possible without losing efficiency;

           i.e., the most abstract form such that when specialized back to the concrete case

           the result is just as efficient as the original algorithm.

  • 當向上的泛化不能滿足所有的演算法需求,另外提出一個更泛化的版本,但是確保當有特定的需求時能夠自動調用那個特化的效率最高的版本。
  • Providing more than one generic algorithm for the same purpose and at the same level of abstraction,

                     when none dominates the others in efficiency for all inputs.

                     This introduces the necessity to provide sufficiently precise characterizations of the domain for which each algorithm is the most efficient.

  • 概念和模型
考慮 swap函數
template <class T>void swap( T& a, T& b) {T tmp = a;a = b;b = tmp;}
當執行swap,執行個體化的時候,模板參數(place holder)T變成一個實際參數類型。但是編譯成功的前提是該實參支援拷貝構造和賦值操作。
注意編譯器只做文法上的檢查,具體你的拷貝建構函式在語義上是否正確由程式員自己確保。
我們將一系列的需求集合稱為一個概念concep,如果一個實參滿足了一個概念的所有需求requirments,我們稱它為給概念的一個模型model.
在swap的例子中,int是可複製概念的一個模型。int is a model of the concept Assignable.
Common basic concepts

 

 

與物件導向模型就行對比,概念conepts類比與虛基類(介面),模型model類比與子類dervied classes(實現).

  • 基於iterator迭代器的泛型演算法

演算法抽象化的核心是泛型程式設計。一個方面就是將訪問資料的介面用一個簡單泛化的概念來實現。一個概念就是迭代器,它是指標的抽象。迭代器,可以指向資料,可以遍曆容器中的資料。

//container對iterator的支援,[begin,end)區間訪問,記住區間都是採用前閉後開的形式,即最有有一個passs the end 標誌位,方便遍曆. while(!= pass_the_end_pos)
template <class T>  class list {void push_back( const T& t); // append t to list.typedef ... iterator;iterator begin();iterator end();};
//演算法並不是為特定容器而寫,採用iterator
template <class InputIterator, class T>bool contains( InputIterator first, InputIterator beyond, const T& value){while ((first != beyond) && (*first != value))++first;return (first != beyond);}

我們可以將上面的演算法用於數組,iterator就是指標。

int a[100];// ... initialize elements of a.bool found = contains( a, a+100, 42);
可以用在數組的一部分進行
bool in_first_half = contains( a, a+50, 42);bool in_third_quarter = contains( a+50, a+75, 42);也可以用在list容器上
list<int> ls;// ... insert some elements into ls.bool found = contains( ls.begin(), ls.end(), 42);
//泛化的copy演算法
template <class InputIterator, class OutputIterator>OutputIterator copy( InputIterator first, InputIterator beyond,OutputIterator result){while (first != beyond)*result++ = *first++;return result;}
//從數組a1,拷貝到數組a2
int a1[100];int a2[100];// ... initialize elements of a1.copy( a1, a1+100, a2);
注意如果從a拷貝到list ls而ls當前是空的,則不能達到目的,因為ls.beign() == ls.end()
STL解決這類問題的方法是提供概念之間的配接器。這裡的配接器back_inserterOutput iteraoter概念的一個model
也就是說它支援資料的寫入,它以list容器作為輸入,當有資料寫入iteraor的時候,其賦值函數=被調用,而=被實現為向其所擁有的容器尾部添加該元素
list<int> ls;copy( a1, a1+100, back_inserter(ls));
後面會給出back_inserter的實現。

同樣的在STL裡面也存在C++ IO/STREAM和iterator之間的適配器,

copy( istream_iterator<int>(cin), istream_iterator<int>(),ostream_iterator<int>( cout, "\n"));

這裡將把從標準輸入得到的所有整型數字輸出到標準輸出,並且數字之間輸出\n。istream_iterator<int>() 表示這個區間的尾部past-the-end position for this range

  • 一個部分實現的iterator
template <class T>class Const_value {T t;public:// Default Constructible !Const_value() {}explicit Const_value( const T& s) : t(s) {}// Assignable by default.// Equality Comparable (not so easy what that should mean here)bool operator==( const Const_value<T>& cv) const { return ( this == &cv); }bool operator!=( const Const_value<T>& cv) const { return !(*this == cv); }// Trivial Iterator:const T& operator* () const { return  t; }const T* operator->() const { return & operator*(); }// Input IteratorConst_value<T>& operator++() { return *this; }Const_value<T>  operator++(int) {Const_value<T> tmp = *this;++*this;return tmp;}};

 

這個iterator會指向一個常量,並且訪問的區間無限大。

int a[100];Const_value<int> cv( 42);copy_n( cv, 100, a);  // fills a with 100 times 42.

  • 函數對象

所謂函數對象也就是仿函數,就是類內部重載實現了operator ()成員函數的類的對象執行個體。

與函數指標相比較,函數對象有很大的優越性,更適合做泛型函數的參數。

  1. 函數對象可以持有自己的局部狀態。類對象,可以有自己的變數。(本小節後面給出例子)
  2. 函數對象在組件技術中有可適配性,可以將某些修飾條件加在其上改變其狀態。(後面的小節給出例子)

函數對象的用法:

template <class T>struct equals {bool operator()( const T& a, const T& b) { return a == b; }};
template <class InputIterator, class T, class Eq>bool contains( InputIterator first, InputIterator beyond, const T& value,Eq eq ) {while ((first != beyond) && ( ! eq( *first, value)))++first;return (first != beyond);}
int a[100];// ... initialize elements of a.bool found = contains( a, a+100, 42, equals<int>());

現在由於函數對象可以持有局部狀態,我們可以實現一個帶有誤差範圍的equals:

我覺得這個例子才有意義,不然沒必要加equals函數作為一個contains函數的參數。

template <class T>struct eps_equals {T epsilon;eps_equals( const T& eps) : epsilon(eps) {}bool operator()( const T& a, const T& b) {return (a-b <= epsilon) && (b-a <= epsilon);}};bool found = contains( a, a+100, 42, eps_equals<int>(1));

這樣顯示尋找成功,如果a中存在41,42或者43.

template <class T>struct count_equals {size_t& count;count_equals( size_t& c) : count(c) {}bool operator()( const T& a, const T& b) {++count;return a == b;}};size_t counter = 0;bool found = contains( a, a+100, 42, count_equals<int>(counter));// counter contains number of comparisons needed.

這個例子中conter就記錄了尋找到的次數。

  • Iterator traits

 

參見efc++ 條款47.
struct iterator_over_ints {typedef  int  value_type;// ...};
template <class Iterator>struct iterator_traits {typedef  typename Iterator::value_type  value_type;// ...};

iterator_traits< iterator_over_ints >::value_type

//對指標的偏特化
template <class T>struct iterator_traits<T*> {typedef  T  value_type;// ...};
iterator_traits< int* >::value_type
iterator traits 同時也定義了difference_type,iterator_category,pointer類型,以及
refernce類型。
利用traits技術我們可以判定資料的類型從而採取適當的措施。

template <class InputIterator, class T, class Eq >
bool contains( InputIterator first, InputIterator beyond, const T& value,
      Eq eq = equals<typename iterator_traits<InputIterator>::value_type>()) {
    while ((first != beyond) && ( ! eq( *first, value)))
        ++first;
    return (first != beyond);
}

// Alternative solution to default using overloaded dispatch function
//
//template <class InputIterator, class T>
//bool contains( InputIterator first, InputIterator beyond, const T& value) {
//    typedef typename iterator_traits<InputIterator>::value_type value_type;
//    typedef equals<value_type> Equal;
//    return contains( first, beyond, value, Equal());
//}

現在對於帶有eq函數參數的contains也可以這麼用了,其實就是因為a 是 int*,對於那個對指標偏特化的traits,得到a指向的資料類型int。其它的iterator類似。

assert(   contains( a, a+6, 42));
assert(   contains( a, a+3, 42));

STL 在其它很多地方大量應用traits手法, char_traits to define the equality test and other operations for a character type.

In addition, this character traits class is used as a template parameter for the basic_string class template, which allows the

 adaption of the string class to different character sets.

    • 實現可配接的函數對象。

     

     

     

    本節內容另可參考effective stl 第40條,若一個類是函數子,則應該使它可配接。

    可配接特性是函數對象專屬的,函數指標則不能。A function pointer can be a valid model for a function object, but it cannot be a valid model of an adaptable function object.

    在需要函數對象的地方函數指標也可以勝任,但是如果需要一個可配接函數對象,那麼函數指標無能為力。

    前面的函數對象equals from above could be derived from std::binary_function to declare the appropriate types.

    #include <functional>template <class T>struct equals : public std::binary_function<T,T,bool> {bool operator()( const T& a, const T& b) { return a == b; }

     

    template <class Arg1, class Arg2, class Result>struct binary_function {typedef Arg1   first_argument_type;typedef Arg2   second_argument_type;typedef Result result_type;};

     

    從binary_function在STL中的定義,我們可以看成equals繼承binary_function,其實就是告訴它及以後的應用程式我的兩個輸入的參數類型是什麼,輸出參數的類型是什麼。

    先看一個應用執行個體,not1的運用

    Example

    // not1 example#include <iostream>#include <functional>#include <algorithm>using namespace std;struct IsOdd : unary_function<int,bool> {bool operator() (const int& x) const {return x%2==1;}};int main () {int values[] = {1,2,3,4,5};int cx;cx = count_if ( values, values+5, not1(IsOdd()) );cout << "There are " << cx << " elements with even values.\n";return 0;}

    Output:

    There are 2 elements with even values.

          計算values[]中偶數的數目,但是我們只有IsOdd函數,在不想再寫IsEven的情況下,利用not1配接器我們可以方便的直接用
          not1(IsOdd)代替IsEven函數。但是要注意我們定義的IsOdd需要繼承unary_function<int,bool>來具有可配接的特性,否則
          無法使用not1。這裡unary_function指有一個輸入參數,一個輸出參數的函數對象的配接起。為什麼需要繼承它呢。
          看一下not1的實現:
    template <class Predicate>inline unary_negate< Predicate>not1( const Predicate& pred) {return unary_negate< Predicate>( pred);}
    可以看到not1隻提供一個介面,算是unary_negate的一個helper函數,它內部調用unary_negate< Predicate>( pred)實現。
    template <class Predicate>class unary_negate: public unary_function< typename Predicate::argument_type, bool> {protected:Predicate pred;public:explicit unary_negate( const Predicate& x) : pred(x) {}bool operator()(const typename Predicate::argument_type& x) const {return ! pred(x);}};
    這裡要調用!prd(x)我們需要知道x的類型,operator()(const typename Predicate::argument_type& x),這個類型由Predicate提供,具體在這裡也就是由實參IsOdd提供。
    not1(IsOdd())則Predicate被實參化為類型IsOdd.所以我們需要IsOdd類提供它的operator()操縱的資料的類型。這也就是為什麼我們需要繼承unary_function<int,bool>。
    內部實現上的繁瑣換來使用者應用的方便,極大的靈活性。如果操作的函數對象是有兩個輸入參數的則要用not2,同時函數對象要繼承binary_function.
    具體應用程式用iterator遍曆區間,假定iterator iter,對每個遍曆到的資料,調用
    not1(IsOdd())(*iter)->unary_negate(IsOdd())(*iter)-> !IsOdd()(*iter) //IsOdd()是一個臨時的函數對象
    再看bind2nd的應用執行個體
    int main( int argc, char** argv) {if ( argc != 2)throw( "usage: remove_if_divides integer\n");remove_copy_if( istream_iterator<int>(cin), istream_iterator<int>(),ostream_iterator<int>(cout, "\n"),not1( bind2nd( modulus<int>(), atoi( argv[1]))));return 0;}

    這個例子是將從標準輸入輸入的所有整數選擇合格拷貝到輸出。這裡的條件是不能被程式輸入參數atoi( argv[1]))所整除。

    bind2nd也是一個hepler函數,實際產生一個binder2nd函數對象。它接受一個bianry function對象,一個固定值,返回一個unary function對象,兩個輸入參數變為一個輸入參數,原來的

    第二個參數是被綁定一個固定值。

    關於bind2nd

    Return function object with second parameter binded

    This function constructs an unary function object from the binary function object op by binding its second parameter to the fixed value x.

    template < class Operation, class Tp>inline binder2nd< Operation>bind2nd( const Operation& fn, const Tp& x) {typedef typename Operation::second_argument_type Arg2_type;return binder2nd< Operation>( fn, Arg2_type(x));}
    template <class Operation>class binder2nd: public unary_function< typename Operation::first_argument_type,typename Operation::result_type> {protected:Operation op;typename Operation::second_argument_type value;public:binder2nd( const Operation& x,const typename Operation::second_argument_type& y): op(x), value(y) {}typename Operation::result_typeoperator()(const typename Operation::first_argument_type& x) const {return op(x, value);}};
         因此 not1(bind2nd(modulus<int>(), num))(*iter) –> !bind2nd(modulus<int>(), num)(*iter) 
             –>!binder2nd(modulus<int>(), num)(*iter)->!modulus<int>(*iter, num)
    • 配接器back_inserter的實現

    template <class InputIterator, class OutputIterator>

    OutputIterator copy( InputIterator first, InputIterator beyond, OutputIterator result){

        while (first != beyond) *result++ = *first++;

        return result;

    }

    list<int> ls;copy( a1, a1+100, back_inserter(ls));
     
    //back_inserter又是一個helper函數,內部調用back_insert_iterator
    template <class Container>inline back_insert_iterator<Container> back_inserter(Container& x) {return back_insert_iterator<Container>(x);}

     

    template <class Container>class back_insert_iterator {protected:Container* container;public:typedef Container           container_type;typedef output_iterator_tag iterator_category;typedef void                value_type;typedef void                difference_type;typedef void                pointer;typedef void                reference;explicit back_insert_iterator(Container& x) : container(&x) {}back_insert_iterator<Container>&operator=(const typename Container::value_type& value) {container->push_back(value);  //key magic is here!return *this;}back_insert_iterator<Container>& operator*()     { return *this; }back_insert_iterator<Container>& operator++()    { return *this; }back_insert_iterator<Container>& operator++(int) { return *this; }};
    • 編譯時間根據迭代器類型進行函數分配(重載)

    struct input_iterator_tag {};struct output_iterator_tag {};struct forward_iterator_tag : public input_iterator_tag {};struct bidirectional_iterator_tag : public forward_iterator_tag {};struct random_access_iterator_tag : public bidirectional_iterator_tag {};

    An iterator is assumed to have a local type iterator_category that is defined to be one of these tags.

    struct Some_iterator {typedef forward_iterator_tag iterator_category;// ...};

    This iterator category is accessed using iterator traits. Now we can implement a generic distance function (original implementation as it is in the STL):

    template <class InputIterator>inline typename iterator_traits<InputIterator>::difference_type__distance( InputIterator first, InputIterator last, input_iterator_tag) {typename iterator_traits<InputIterator>::difference_type n = 0;while (first != last)++first; ++n;return n;}template <class RandomAccessIterator>inline typename iterator_traits<RandomAccessIterator>::difference_type__distance( RandomAccessIterator first, RandomAccessIterator last,random_access_iterator_tag) {return last - first;}template <class InputIterator>inline typename iterator_traits<InputIterator>::difference_typedistance( InputIterator first, InputIterator last) {typedef typename iterator_traits<InputIterator>::iterator_categoryCategory;return __distance(first, last, Category());}

    相關文章

    聯繫我們

    該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

    如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

    A Free Trial That Lets You Build Big!

    Start building with 50+ products and up to 12 months usage for Elastic Compute Service

    • Sales Support

      1 on 1 presale consultation

    • After-Sales Support

      24/7 Technical Support 6 Free Tickets per Quarter Faster Response

    • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.