1. 程式人生 > >Effective STL學習

Effective STL學習

引言

以下為個人的一些讀書心得,對於部分比較淺顯易懂的就不再贅述,只是日常使用中不太會留意到的加以解釋。

第一章 容器

第一條:慎重選擇容器型別

第二條:不要試圖編寫獨立於容器型別的程式碼

第三條:確保容器中的物件拷貝正確而高效

第四條:呼叫empty而不是檢查size()是否為0

舉例:list的size()可能需要遍歷整個連結串列才能得到,而empty卻不需要。

第五條:區間成員函式優先於與之對應的單元素成員函式

比較三種形式:
1.

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

2.

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

3.

vector<Widget> v1, v2;
...
v1.clear();
copy(v2.begin() + v2.size() / 2, v2.end(), back_inserter(v1));

比較三種形式,2,3比較類似(3的內部實現幾乎和2相同,故合併討論,僅討論1和2的區別)。
書中講了三點:a.多次對insert的呼叫(這個我認為幾乎可以忽略);b.針對於當前例子,針對v1多次的push_back可能導致vector容量多次增加,而通過區間成員函式會一次性增加到所需容量;c.insert或者push_front比較明顯,每次呼叫均會導致後面內容的移動,而通過區間成員函式幾乎總可以一次性移動到位。

最後書中給出了幾種區間函式:
1. 區間建立 container::container(InputIterator begin, InputIterator end);
2. 區間插入 void container::insert(iterator position, InputIterator begin, InputIterator end);
3. 區間刪除 iterator container::erase(iterator begin, iterator end);// 序列容器 void container::erase(iterator begin, iterator end);// 關聯容器 // 這些不同導致在迴圈中刪除容器元素的區別,在後麵條例中也有提及,這裡支出關聯容器不返回一個迭代器是由於這樣操作將導致不可接受的效能負擔,對此作者也不甚贊同。
4. 區間賦值 void constainer::assign(InputIterator begin, InputIterator end);

第六條:當心c++編譯器最煩人的分析機制

ifstream dataFile("ints.dat");
list<int> data(istream_iterator<int>(dataFile), istream_iterator<int>());

雖然這段程式碼當使用到data時即會報錯,但是還是可以分析下以上程式碼被解析的語義:
istream_iterator(dataFile), istream_iterator()將被解析為兩個引數(第一個引數名為dataFile,第二個引數名進行了省略);
類似:int f(double (d)); <==> int f(double d);
最終可以將istream_iterator(dataFile)通過“()”包裹即可

第七條:如果容器中包含了通過new操作建立的指標,切記在容器物件析構錢江指標delete掉

第八條:切勿建立包含auto_ptr的容器物件

第九條:慎重選擇刪除元素的方法

以下附上了書中的總結,其中第三條需要注意下,這個也可以參見條例五區間刪除。
這裡寫圖片描述

第十條:瞭解分配子的約定和限制

這條在定製allocator時需要注意

第十一條:理解自定義分配子的合理用法

第十二條:切勿對STL容器的執行緒安全性有不切實際的依賴

第二章 vector和string

第十三條:vector和string優先於動態分配的陣列

第十四條:使用reserve來避免不必要的重新分配

第十五條:注意string實現的多樣性

//TODO 有空可以多檢視下各種知名的實現,或者參照書中提出的自己實現一下

第十六條:瞭解如何把vector和string資料傳給舊的API

  1. vector傳遞給指標: &vd[0],vector在c++標準中保證了它和陣列記憶體上保持一致(注意不要使用vd.begin(),因為沒有強制要求vector迭代器的就採用指標實現);
  2. string s; s.c_str();
  3. string,set,list需要採用c api均可以通過vector傳遞,在通過assign進行轉換。(注意:vector通過c api填充值後需要重新resize
    這裡寫圖片描述

第十七條:使用“swap技巧”出去多餘容量

// 使容量最小
vector<Contestant> v;
string s;
...
vector<Contestant>(v).swap(v);
string(s).swap(s);
// 清空並使容量最小
vector<Contestant> v;
string s;
...
vector<Contestant>().swap(v);
string().swap(s);

第十八條:避免使用vector

由於vector進行了優化,內部使用bit進行儲存,所以vector::operator[]返回和其它vector有所不同,書中建議避免使用,本人對此知識有限,當遇到它的弊端再詳細探討。

第三章 關聯容器

第十九條:理解相等和等價的區別

這個主要對於關聯容器中的插入,因為關聯容器要要排序(考慮set),插入一個物件,總能考慮是在參考節點前面還是在後面(這種通過operator<或者是使用者定義的判別式進行判斷),如果a,b: a < b == false && b < a == false則a、b等價,則ab不能同時插入進set,所以是否等價為插入的唯一標準
但有時候等價和相等並不一致:如果set中插入元素判別式不區分大小寫,而元素相等需要精確匹配。則A、a不能同時插入進set,而且set的find採用等價規則(注意和std ::find採用相等規則,兩者可能出現不太一致的現象),舉例:
這裡寫圖片描述

第二十條:為包含指標的關聯容器指定比較型別

今天到此為止,後期繼續新增