Effective STL 第一章 第9條

2021-06-20 03:23:23 字數 3113 閱讀 2256

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

先寫總結,在寫例子:

1、要刪除容器中有特定值的所有物件:

如果容器是vector, string或deque,則使用erase-remove習慣用法

如果容器是list,則使用list::remove

如果容器是乙個標準關聯容器,則使用它的erase成員函式

2、要刪除容器中滿足特定判斷式(條件)的所有物件:

如果容器是vector、string和deque,則使用erase-remove-if用法

如果容器是list, 則使用list::remove_if

如果容器是乙個標準關聯容器,則使用remove_copy_if和swap,或者寫乙個迴圈來遍歷容器中的元素,當把迭代器傳給erase時,要對它進行字尾遞增。

3、要在迴圈內部做某些(除了刪除物件之外的)操作:

如果容器是乙個標準序列容器,則寫乙個迴圈來遍歷容器中的元素,每次呼叫erase時,要用它的返回值更新迭代器

如果容器是乙個標準關聯容器,則寫乙個迴圈來遍歷容器中的元素,每次迭代器傳給erase時,要對它進行字尾遞增

以下是例子:

假定乙個容器c,包含int型別,containerc;

問題1:當要刪除c中所有值為1963的元素

(1)如果是乙個連續記憶體容器(vectordeque string),使用erase-remove方法

remove操作並沒有移除元素,而是將後面的元素覆蓋刪除元素,

返回新區間的邏輯終點,即下乙個沒有被刪除的位置

(注意這裡會使原來及其後面的迭代器失效,因為指向了不正確的元素)

erase刪除兩個迭代器之間的元素

c.erase(remove(c.begin(), c.end(), 1963),c.end());

(2)對於list,這一辦法同樣適用,但是適用list成員函式remove更高效

c.remove(1963);

(3)當c是標準關聯容器(set multiset map multimap)時,不能使用remove操作,

因為沒有這個成員函式,使用remove演算法可能會覆蓋容器的值,同時可能會破壞容器

正確方法是 呼叫erase

c.erase(1963);    //只需要對數時間開銷,序列容器需要線性時間

問題2:如果是使下面判斷式返回true的每乙個物件

bool badvalue(int x);  //返回x是否是「壞值」

(1) 對於序列容器(vectorstring deque list),對remove的呼叫換成remove_if就可以

c.erase(remove_if(c.begin(), c.end(),badvalue), c.end());

list:c.remove_if(badvalue)

(2)對於標準關聯容器,解決方案有兩種:

第一種:利用remove_copy_if把我們需要複製的值複製到乙個新容器中,然後把原來容器的內容和新容器的內容相互交換:

assoccontainerc;          //c是標準關聯容器

assoccontainergoodvalue;  //儲存不被刪除的值的臨時容器

remove_copy_if(c.begin(), c.end(),  //把不被刪除的值從c複製到goodvalues中

inserter(goodvalues,goodvalues.end()), badvalue);

c.swap(goodvalue);       //交換c和goodvalues的值

缺點是:需要複製所有不被刪除的元素

不能直接從原始的容器中刪除元素,因為容器沒有提供類似remove_if的成員函式。

第二種:可以寫乙個迴圈,在遍歷過程中刪除元素。

乙個直截了當的方法:

for(assoccontainer::iterator i =c.begin(); i != c.end(); ++i )

if(badvalue(*i))

c.erase(i);

但是這樣會導致不確定的行為,當容器中的乙個元素被刪除時,指向該元素的所有迭代器都變得無效,c.erase(i)之後,i變為無效值。

為了避免這個問題,要確保在呼叫erase之前,有乙個迭代器指向c中的下乙個元素。

最簡單的方法是,對i使用字尾++

for(assoccontainer::iterator i =c.begin(); i != c.end(); (什麼都不做) )

if(badvalue(*i))

c.erase(i++);      //這樣在執行erase之前已經對i遞增為新的值,舊的值刪除

//變為了無效值,但i已經成為新值

else

++i;

問題3:如果我們不僅要刪除使badvalue返回true的元素,還當元素被刪除時向日誌檔案記錄一條資訊

ofstream logfile;

for(assoccontainer::iterator i =c.begin(); i != c.end(); (什麼都不做) )

if(badvalue(*i)) {

logfile << 「eraseing」 << *i << 『\n』;  //寫日誌檔案

c.erase(i++);        //關聯容器對erase的返回值是void

else

++i;

這種方法對vector string  deque無效,會導致不確定行為,因為這種容器呼叫erase不僅會使指向被刪除元素的迭代器無效,也會使被刪除元素之後的所有迭代器失效(因為容器位置變了,原來指向的內容已經變了),++i,--i都會無效。

所以對於vector string  deque,利用erase的返回值。返回值是我們所需要的,一旦erase完成,它是指向緊隨被刪除元素的下乙個元素的有效迭代器。

for(assoccontainer::iterator i =c.begin(); i != c.end(); (什麼都不做) )

if(badvalue(*i)) {

logfile << 「eraseing」 << *i << 『\n』;  //寫日誌檔案

i =c.erase(i); 

else

++i;

對於list來說,一般是對list採用和vector string deque相同的方式。

Effective STL 第一章 容器(二)

第3條 確保容器中的物件拷貝正確而高效 1,copy in,copy out是stl的工作方式,即向容器中加入物件時,存入容器的是你指定物件的拷貝,從容器中取出物件時,得到的是容器中所儲存的物件的拷貝。2,拷貝物件時stl的工作方式,當對vector,string或deque進行元素的插入或刪除操作...

Effective stl 第一章 第10條

這隔了一星期沒看 看的 感覺好久沒看書 好久沒寫 了 這一不學習就感覺很難進入狀態了 我要收心學習了!收心收心 這一章 沒仔細看 也沒看懂。先記一點 回來再補 第10條 了解分配子 allocator 的約定和限制 最後的總結 1 你的分配子是乙個模板,模板引數t代表你為它分配記憶體的物件的型別。2...

python第一章筆記 第一章 基礎

參與除法的兩個數中有乙個數為浮點數,結果也為浮點數 如 1.0 2,1 2.0,1.0 2.0 python print 1.0 2 結果 0.5 print 1 2.0 結果 0.5 print 1.0 2.0 結果 0.5 整數 整數,計算結果的小數部分被截除,只保留整數部分 不會四捨五入 如 ...