STL高效程式設計 三 使容器元素的拷貝正確和高效

2021-05-02 20:25:27 字數 2004 閱讀 8242

首先,你必須要明白的是,容器容納著許多物件,但不是你傳給它的那些原始物件,而是物件的拷貝。

此外,當你從容器中獲取乙個物件時,得到的不是容器裡的那個物件,而是物件的拷貝。同樣的,當你向容器中新增乙個物件時(通過insert或push_back),新增到容器的是你給的物件的拷貝。copy進去,copy出來,這就是stl的方式。所以,stl要求物件必須是可拷貝的。物件被存到容器裡之後,對它的拷貝並不少見。如果你從vector、string或deque中插入或刪除了元素,現有元素會移動(拷貝)。如果你使用了任何排序演算法,一般的排序演算法都要求交換,交換是需要拷貝的,這種例子很多。

你可能會對所有這些拷貝是怎麼完成的感興趣。這很簡單,乙個物件通過使用它的拷貝成員函式來拷貝,特別是它的拷貝建構函式和它的拷貝賦值建構函式。這就是傳說中的big 3. 對於使用者自定義類,比如widget,這些函式傳統上是這麼宣告的:

class widget ;
如果你自己沒有宣告這些函式,你的編譯器會在需要的時候為你生成它們。拷貝內建型別(比如int、指標等)也始終是通過簡單地拷貝他們的二進位制bit值來完成的。(有關拷貝建構函式和賦值操作符的詳細情況,請參考任何c++的介紹性書籍。我想你推薦c++ programming和inside c++ project model.這兩本書講的很透徹)。

如果你用乙個拷貝操作很昂貴的物件填充乙個容器,那麼乙個簡單的操作——把物件放進容器也會被證明為是乙個效能瓶頸。容器中移動越多的東西,你就會在拷貝上浪費越多的記憶體和cpu時鐘週期。此外,如果你定義了的有問題拷貝建構函式,這也會直接影響到容器。

當然由於繼承的存在,拷貝會導致切割片(slicing)。那就是說,如果你以基類物件建立乙個容器,而你試圖插入派生類物件,那麼當物件(通過基類的拷貝建構函式)拷入容器的時候物件的派生部分會被刪除。

vectorvw; 

class specialwidget: // specialwidget從上面的widget派生

public widget ;

specialwidget sw;

vw.push_back(sw); // sw被當作基類物件拷入vw, 當拷貝時它的子類部分丟失了

切片問題暗示了把乙個派生類物件插入基類物件的容器幾乎總是錯的。如果你希望結果物件表現為派生類物件,比如,呼叫派生類的虛函式等,總是錯的。(關於slicing問題更多的背景知識,請參考《effective c++》條款22。)

乙個使拷貝更高效、正確而且避免分割問題的簡單的方式是建立指標的容器而不是物件的容器。也就是說,不是建立乙個widget的容器,建立乙個widget*的容器。拷貝指標很快,而且不會有額外的開銷(僅僅是簡單的的二進位制值的拷貝),而且當指標拷貝時不會產生slicing的問題。美中不足的是,指標的容器有帶來了另外乙個令人頭疼的問題,你需要自己手動來刪除這些指標。如果你不想手動來刪除指標,在權衡效率、正確性和slicing這些因素時,智慧型指標會是乙個不錯的解決方案。

如果所有這些使stl的拷貝機制聽起來很瘋狂,就請重新想想。是,stl進行了大量拷貝,但它一般設計時,會盡量避免不必要的物件拷貝,實際上,它的實現也盡量避免不必要的物件拷貝。和c和c++內建容器的行為做個對比,下面的陣列:

widget w[maxnumwidgets];		// 建立乙個大小為maxnumwidgets的widgets陣列, 預設構造每個元素
即使我們一般只使用其中的一些或者我們立刻使用從某個地方獲取(比如,乙個檔案)的值覆蓋每個預設構造的值,這也得構造maxnumwidgets個widget物件。使用stl來代替陣列,你可以使用乙個可以在需要的時候增長的vector:

vectorvw;  // 建立乙個0個widget物件的vector, 需要的時候可以擴充套件
我們也可以建立乙個可以足夠包含maxnumwidgets個widget的空vector,但沒有構造widget:

vectorvw;

vw.reserve(maxnumwidgets);

和陣列對比,stl容器更靈活。它們只建立(通過拷貝)你需要的個數的物件,而且它們只在你指定的時候做。是的,我們需要知道stl容器使用了拷貝,但是別忘了乙個事實:相比陣列來說它們仍然是乙個進步。

STL容器刪除元素的陷阱

今天看scott meyers大師的stl的用法,看到了我前段時間犯的乙個錯誤,發現我寫的 和他提到錯誤 幾乎一模一樣,有關stl容器刪除元素的問題,錯誤的 如下 std vectormfriendlist std vector iterator iter mfriendlist.begin for...

STL中map容器的元素插入

stl中的map容器是我經常用的,但是因為map跟別的容器不太一樣,每次用的時候對於map中元素的插入方式總是忘卻,故而發篇博文,提醒我也提醒所有人map容器的三種插入方式 第一種 用insert函式插入pair資料。下面舉例說明 include include include using name...

STL容器的元素型別傳遞測試

stl的容器模板類在新增元素的時候,採用拷貝構造,並且是淺拷貝,而不是直接傳遞指標,這意味這系統要多開闢一塊記憶體來滿足容器的使用。如果要正確使用stl容器對各種物件操作要注意過載拷貝建構函式,也就是變成深拷貝。測試 使用到了vector向量,而每次他都呼叫當前元素相應個數的析構以及拷貝構造。這其中...