1. 程式人生 > >Effective_STL 學習筆記(四十三) 儘量用演算法呼叫代替手寫迴圈

Effective_STL 學習筆記(四十三) 儘量用演算法呼叫代替手寫迴圈

 

每一個演算法接受至少一對用來指示將被操作物件區間的迭代器,比如,min_element 可以找出此區間中的最小的值,而 accumulate 則對區間內的元素作某種形式的整體求和運算,partition 將區間內的元素分割為滿足和不滿足某某判決條件的兩個部分。演算法執行時,他們進行檢查指示給它的區間中的每一個元素,並且按照所期望的方式進行: 從區間中的起點迴圈到結束點。有一些演算法,比如 find 和 find_if,可能在遍歷完成前就返回了,但是即使是這些演算法,內部都包含一個迴圈。

 

演算法內部是一個迴圈,STL 的廣泛涉及面意味著,很多要用迴圈實現的任務,可以改用演算法實現。

1   class Widget
2   {
3   public:
4     void redraw() const;
5     . . .
6   }

使用迴圈

1   list<Widget> lw;
2   . . .
3   for(list<Widget>::iterator i = lw.begin(); i != lw.end(); i++)
4     i->redraw();

使用 for_each 演算法

1   for_each( lw.begin(), lw.end(), mem_fun_ref(&Widget::redraw) );

有三個理由:

  效率: 演算法通常比程式設計師產生的迴圈更高效

  正確性: 寫迴圈時比呼叫演算法更容易產生錯誤

  可維護性: 演算法通常使程式碼比相應的顯示迴圈更乾淨、更直觀

 

 

效率:演算法往往比迴圈減少了大量的函式呼叫次數(上例中,多次呼叫 lw.begin()),STL實現者知道 begin 和 end 用的很頻繁,多以儘可能把他們實現得最高效,幾乎肯定的是 inline 它們。實現者可以利用知道的容器具體實現優勢,用庫的使用者無法採用方式來優化遍歷。所有STL演算法使用的電腦科學都比一般的 C++ 程式設計師能拿的出的演算法更復雜。幾乎不可能被打敗的 sort 及同組演算法(比如,stable_sort(), nth_element()等);適用於有序區間的搜尋演算法(比如,binary_search, lower-bound等);就算很平凡的任務,比如從連續記憶體容器中除去一些物件,使用 erase-remove 慣用法都比絕大多數程式設計師寫得更高效.

 

正確性:寫迴圈時,比麻煩的事在於確保所有使用的迭代器(a)有效,並且(b)指向所期望的地方,假設有一個數組,想獲得每一個元素,把它加上41,然後將結果插入一個 deque 的前端

1   size_t fillArray( double* pArray, size_t arraySize );//函式向陣列寫入資料,返回寫入double個數
2   double data[maxNumDoubles];  
3   deque<double> d;
4   . . .
5   size_t numDoubles = fillArray( data, maxNumDoubles );
6   for(size_t i = 0; i < numDoubles; i++)
7     d.insert( d.begin(), data[i] + 41 );  // 每個資料在 d 的前端插入 data[i] + 41        
8                               // 這段程式碼有一個bug!

這可以執行,但是插入元素與在 data 中對應的元素是反序的

不想反序可能想這樣修改:

1   deque<double>::iterator insertlocation = d.begin();  // 記下d的起始迭代器
2   for( size_t; i < numDoubles; i++ )
3     d.insert( insertLocation++, data[i] + 41 );// 插入data[i]+41,然後insertLocation遞增
4                                // 這段程式碼也有 bug

這樣使得每次呼叫 deque::insert 後,都導致所有指向 deque 內部的迭代器失效

改為:

1     deque<double>::iterator insertlocation = d.begin();
2     for( size_t i = 0; i < numDoubles; i++ )
3     {
4         insertLocation = d.insert( insertLocation, data[i] + 41 );
5       ++insertLocation;
6     }

 

呼叫演算法 transform:

1   transform(data, data+numDoubles, inserter(d, d.begin()), bind2nd(plus<double>(), 41));

把迭代器扔給演算法,讓他們考慮操縱迭代器時的各種詭異