effective stl(容器部分總結)

還是很喜歡effective部分的書,看了好幾遍,這裏把stl中和容器相關的一些基本的注意的點進行介紹總結,之後對迭代器等進行總結

1 對序列容器中需要逐個刪除的時候,不能像關聯容器那樣事先對迭代器進行++操作,因爲刪除一個迭代器,會使他自己無效,後面的迭代器也無效,所以應該保存刪除erase返回的下一個指針的值。而關聯容器中並不會導致後面的迭代器無效的情況

2 在stl中如果對容器內對象使用了new操作,一定要釋放掉,因爲析構函數沒辦法完成,這裏最好使用智能指針,但是不要使用auto_ptr的指針,因爲他的拷貝,會使背被拷貝的對象賦值爲NULL可能導致無法預料的問題

3 如果希望減少在替換容器的類型的時候需要修改的代碼,那麼應該把容器進行typedef的替換,同時最好直接隱藏到一個類當中,進行使用接口進行處理

4 使用區間成員函數,比如assign,區間的構造函數,區間的insert函數,區間的erase函數,都能比循環調用單元素對應的函數好,從函數調用,元素之間賦值位置變化,還有內存的分配問題進行考慮

5 對於序列容器,list容器,還有關聯容器,刪除元素的使用應該選用不同的刪除函數,具體參見條款9,注意這裏是刪除某個單獨的元素,如果是直接刪除一個區間的數據,那麼直接利用erase就可以

6 許多string的背後使用了計數的功能,消除沒有必要的內存分配和字符拷貝,提高了效率。但是如果是在多線程的情況下,那麼久必須注意讀寫的一致性問題

7 reverse函數只有vector和string有

他可以減少內存重新分配的次數

重點需要區分一下resize和reverse兩個函數

resize:強迫容器改變到包含n個元素的狀態,如果比現在的size()數量小,那麼將末尾的數據析構,如果大那麼利用默認構造函數進行初始化

reverse:強迫容器的容量爲n,和capacity()進行對應,通常導致新的內存分配。如果n比當前的容量小,vector什麼都不做,string將其設爲size()和n中的最大值

一般在聲明容器後就進行reverse的操作,可以預見大小的情況下,之後也就可以預估迭代器是否會失效

8 string有很多的實現方式

但是基本都必須存有下面幾個部分的數據

字符串的大小 

內存的容量capacity

 字符串的值

分配子的一個拷貝

對值的引用計數

不同的實現方式可以會使器是否有引用計數,經常需要複製,或者多線程的情況下可能有用,同時如果是小字符串,是否會進行單獨的油畫,創建時候的動態分配內存的數量等,詳情看第15條數據

9 如果有一個 C API的函數

void dosomething(const int* pInts,size_t numInts);

void dosomething(const char* pInts);

對於vector可以直接傳入&v[0],一般不建議傳入v.begin()因爲他畢竟是一個迭代器,但是傳入的時候需要判斷容器是不是空的

對於string有一個專門的函數,s.c_str(),不需要判斷是否爲空

一般傳入後,最好設爲只讀模式,如果進行了增刪操作,那麼可能會直接影響了之前的容器,導致一些不可預見的影響

如果是C函數初始化容器,都可以利用vector進行一箇中間的轉存的操作,詳情見16條款

10 如果希望將vector,string中的一些多餘的容量進行刪除,erase主要是刪除了容量的大小,但是並沒有減少容量,爲了壓縮到適當的大小利用swap的方法

vector<Contestant>(V2).swap(V2)

string s; string(s).swap(s)

先通過創建臨時變量,只會拷貝存在的元素,然後再交換

同時注意:並不是說之後的內存就剛好是元素的內存和,可能也會有一定的保留

同時:之前的迭代器和,指針,引用之類並不會失效

11 vector中最好不要使用vector<bool>的容器,因爲他並不是一個真正的容器,只是近似於一個容器。他是沒有辦法通過operator []取位置進行操作的。因爲一個bool在實現的時候是按位進行存儲的,只佔有一個二進制位。而爲了適配[]操作,封裝了一個適配器返回的是一個適配類。所以很多麻煩

如果一定要用bool的容器,那麼建議使用deque<bool> (相對於vector函數只是沒有reverse和capacity操作)和bitsize(也是緊湊存儲,事C++標準庫中的函數)



12 在關聯容器中 有兩個概念,相等和等價

非成員函數的find函數一般是利用相等,operator ==基於數據的值是否相等eaqul_to函數

非成員函數的insert操作,因爲要判斷,是否有相同的key了,所以主要是對排序數據中的位置進行判斷的,在其中的等價關係主要是通過operator < 進行判斷的,less函數

每個標準容器利用用戶自己定義的key_comp進行判斷,默認的函數是less函數,可以自己設定,確定了之後只要利用成員函數進行訪問,那麼就能夠將比較進行統一了。

而且最好在和排序相關的關聯容器中使用等價的概念

因爲如果相等的情況下插入兩個其實等價的數據,但是在排序的時候又沒辦法分開排序,那麼就不能按確定的方式訪問key的value值了,類似於multiSet等


但是這裏主要是提到了帶排序的關聯容器,對於不是標準的關聯容器:

hash_set hash_multiset hash_map hash_multimap

這些關聯容器並不是按照排序方式進行存儲的

他們的比較函數就是equal_to


13 複習一下traits的應用


14 如果想打印一個容器中的值,可以用copy函數講值放入一個輸出流中

copy(ssp.begin(),ssp.end(),ostream_iterator<string>(count,”\n”))

還有一個需要注意的地方就是在傳入一些比較函數的時候其實傳入的需要的是一個類型,而不是函數指針,如果是函數指針,直接聲明一個函數就可以了。。。需要注意,如果是一個類型

那麼一般選擇的是一個struct類型後創建一個函數

struct Differ{

template<typename ptrtype>

bool operator()(ptrtype p1,ptrtype p2)

{

return *p1 < *p2;

{

}

調用的時候 set<string* , Differ> ssp


15 感覺21條建議真的是很暈啊,看了幾遍才理清楚

主要是容器中雖然有一個等價的判斷函數比如compare但是最後判斷是否是等價還有一個外層的判斷

!(compare(a1,a2)) && !(compare(a2,a1))

如果compare函數將兩個相等的數判斷爲相等,那麼最終會導致說這兩個數不等價,錯誤

所以對相等的數,compare應該返回false,其實這裏的compare應該理解爲 一個值是否在另一個值的前面,如果兩個值相等,那麼他的值當然沒有位置的關係,所以應該返回false


16 對於set map類型中如果需要修改對應的key

在map中類型是pair<K,V>,其中K是const類型,所以是不允許直接修改的

在set中元素不是const的,因爲類型可能是定義的struct或者 class的類型,在排序的過程中,根據某一元素進行排序,那麼在修改的時候他是不能夠修改的,但是對於別的部分,是可以進行修改的。

因爲如果直接修改,可能會影響之前的排序情況

 如果一定要進行改變,最好的是複製元素,修改,刪除後重新插入


17 在考慮排序的vector的過程中:
因爲map中是按照平衡二叉樹進行存儲的,如果只是想要提高查詢的速度,那麼選用散列的容器(常數時間)遠遠好於標準容器的對數時間,但是如果散列表比較小,可能會使查找的性能比較差

同時在map的存儲中因爲還有一些指針的額外開銷,同時作用兄弟在無力上的存儲也不相鄰。所以如果量很大,比如跨越了多個內存頁面的情況下可能會相對而言比較慢。

相對於排序的vector中,如果採用binary_search可能會更快,但是因爲是vector的情況,所以必須要求數據的增刪的操作很少,同時還得模擬pair 的一些操作,這裏一個注意的地方就是,map中的key是const的,但是在vector中進行模擬的時候是不能定義爲const,因爲會有很多的拷貝操作等


18 對於map的操作,[]操作有不同的意思,如果key在原來的數據結構中沒有,那麼對應時插入操作,如果已經有了,那麼對應的就是修改操作

爲了效率高,

如果,確定知道是插入,最好利用insert操作。因爲如果是[],是先插入一個構造函數對應的pair數據,然後再進行修改的數據,不如直接插入

如果是修改操作,那麼就是直接進行[],因爲如果是利用insert,是先將值[]修改了後insert



發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章