第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 整數 整數,計算結果的小數部分被截除,只保留整數部分 不會四捨五入 如 ...