高效使用 STL 的 11 個條款

僅僅是個選擇的問題,都是STL,可能寫出來的效率相差幾倍;
熟悉如下條款,高效的使用STL;算法

當對象很大時,創建指針的容器而不是對象的容器

  1. STL基於拷貝的方式的來工做,任何須要放入STL中的元素,都會被複制;
    這也好理解,STL工做的容器是在堆內開闢的一塊新空間,而咱們本身的變量通常存放在函數棧或另外一塊堆空間中;爲了可以徹底控制STL本身的元素,爲了能在本身的地盤隨心幹活;這就涉及到複製;
    而若是複製的對象很大,由複製帶來的性能代價也不小 ;
    對於大對象的操做,使用指針來代替對象能消除這方面的代價;編程

  2. 只涉及到指針拷貝操做, 沒有額外類的構造函數和賦值構造函數的調用;函數

    vecttor <BigObj> vt1;
    vt1.push_bach(myBigObj);
    
    vecttor <BigObj* > vt2;
    vt2.push_bach(new BigObj());

注意事項:工具

  1. 容器銷燬前須要自行銷燬指針所指向的對象;不然就形成了內存泄漏;
  2. 使用排序等算法時,須要構造基於對象的比較函數,若是使用默認的比較函數,其結果是基於指針大小的比較,而不是對象的比較;

用empty() 代替size()來檢查是否爲空

由於對於list,size()會遍歷每個元素來肯定大小,時間複雜度 o(n),線性時間;而empty老是保證常數時間;性能

儘可能用區間成員函數代替單元素操做

使用區間成員函數有如下好處:指針

  1. 更少的函數調用
  2. 更少的元素移動
  3. 更少的內存分配

例:將v2後半部的元素賦值給v1:code

單元式操做:server

for (vector<Widget>::const_iterator ci = v2.begin() + v2.size() / 2;
ci != v2.end();
++ci)
v1.push_back(*ci)

使用區間成員函數assign():對象

v1.assign(v2.begin() + v2.size() / 2, v2.end());

使用reserver避免沒必要要的內存分配(for vector)

新增元素空間不夠時,vector會進行以下操做:blog

  1. 分配當前空間的兩倍空間;
  2. 將當前元素拷貝到新的空間中;
  3. 釋放以前的空間;
  4. 將新值放入新空間指定位置;

若是預先知道空間的大小,預先分配了空間避免了從新分配空間和複製的代價;
注:reserve()只是修改了容量,並不是大小,向vector中增長元素仍是須要經過push_back加入;

使用有序的vector代替關聯容器(階段性的操做適用)

對階段性操做的定義:
先作一系列插入、完成以後,後續操做都是查詢;

在階段性的操做下,使用vector有如下優點:

  1. 由於vector有序,關聯容器帶來的有序優點散失;
  2. 都是使用二分法查找的前提下,查詢算法對連續的內存空間的訪問要快於離散的空間;

在map的insert()和operator[]中仔細選擇

插入時,insert效率高;由於operator會先探查是否存在這個元素,若是不存在就構造一個臨時的,而後才涉及到賦值,多了一個臨時對象的構造;

更新時,[]效率更高,insert會創造一個對象,而後覆蓋一個原有對象;而[]是在原有的對象上直接賦值操做;

散列函數的默認比較函數是equal_to,由於不須要保持有序;

儘可能用算法替代手寫的循環

  1. 效率相比手寫更高;
    STL的代碼都是C++專家寫出來的,專家寫出來的代碼在效率上很難超越;
    除非咱們放棄了某些特性來知足特定的需求,可能能快過stl;好比,基於特定場合下的編程,放棄通用性,可移植性;
  2. 不容易出錯;
  3. 使用高層次思惟編程
    相比彙編而言,C是高級語言;一條C語言語句,用匯編寫須要好幾條;
    一樣的,在STL的世界中,咱們也有高層次的術語:
    高層次的術語:insert/find/for_each(STL算法)
    低層次的詞彙:for /while(C++語法)
    用高層次術語來思考編程,會更簡單;

儘可能用成員函數代替同名的算法

  1. 基於效率考慮,成員函數知道本身這個容器和其餘容器有哪些特有屬性,可以利用到這些特性;而通用算法不能夠;
  2. 對於關聯容器,成員函數find基於等價搜索;而通用算法find基於相等來搜索;可能致使結果不同;

使用函數對象代替裸函數做爲算法的輸入參數

由於內聯,在函數對象的方式中,內聯有效,而做爲函數指針時,通常編譯器都不會內聯函數指針指向的函數;即便指定了inline;
好比:

inline bool doubleGreater(double d1, double d2)
{
    return dl > d2;
}
vector<double> v;
...
sort(v.begin(), v.end(), doubleGreater);

這個調用不是真的把doubleGreater傳給sort,它傳了一個doubleGreater的指針。
更好的方式是使用函數對象:

sort(v.begin(), v.end(), greater<double>())

注:《effcient c++》中的實驗結論,使用函數對象通常是裸函數的1.5倍,最多能快2倍多

選擇合適的排序算法

須要排序前思考咱們的必要需求,可能咱們只是須要前多少個元素,這時並不須要使用sort這種線性時間的工具,性能消耗更少的parttition多是更好的選擇;

如下算法的效率從左到右依次遞減:

partition > stable_partition / nth_element / patical_sort / sort / stable_sort

功能說明:

  • partition :將集合分隔爲知足和不知足某個標準兩個區間;
  • stable_partition :partition的穩定版本;
  • nth_element :獲取任意順序的前N個元素;
  • patical_sort :獲取前N個元素,這個N個元素已排序;
  • sort:排序整個區間;
  • stable_sort:sort的穩定版本;

選擇合適的容器

爲何vector不提供push_front()成員方法?由於效率太差,若是有太多從前面插入的需求,就不該該使用vector,而用list;
關心查找速度,首先應該考慮散列容器(非標準STL容器,如:unordered_map,unordered_set);其次是排序的vector,而後是標準的關聯容器;

參考

《effictive STL》
《Efficient C++》

推薦支持

若是你以爲本文對你有所幫助,請點擊如下【推薦】按鈕, 讓更多人閱讀;


Posted by: 大CC | 25JUN,2015
博客:blog.me115.com [訂閱]
微博:新浪微博

相關文章
相關標籤/搜索