国产一级a片免费看高清,亚洲熟女中文字幕在线视频,黄三级高清在线播放,免费黄色视频在线看

打開APP
userphoto
未登錄

開通VIP,暢享免費(fèi)電子書等14項(xiàng)超值服

開通VIP
STL序列式容器中刪除元素的方法和陷阱一 - CyberVisualink的專欄 - CS...

STL序列式容器中刪除元素的方法和陷阱一 收藏
在 STL (標(biāo)準(zhǔn)模板庫)中經(jīng)常會(huì)碰到要?jiǎng)h除容器中部分元素的情況,本人在編程中就經(jīng)常編寫這方面的代碼,在編碼和測試過程中發(fā)現(xiàn)在 STL 中刪除容器有很多陷阱,網(wǎng)上也有不少網(wǎng)友提到如何在 STL 中安全刪除元素這些問題。本文將討論編程過程中最經(jīng)常使用的兩個(gè)序列式容器 vector 、 list 中安全刪除元素的方法和應(yīng)該注意的問題,        其它如 queue 、 stack 等配接器容器( container adapter ),由于它們有專屬的操作行為,沒有迭代器( iterator ),不能采用本文介紹的刪除方法,至于 deque ,它與 vector 的刪除方法一樣。 STL 容器功能強(qiáng)大, but no siliver bullet ,如果你使用不當(dāng),也將讓你吃盡苦頭。

1 .手工編寫 for 循環(huán)代碼刪除 STL 序列式容器中元素的方法
例如,你能看出以下代碼有什么問題?

例 1 :

#include <iostream>

#include <vector>

using namespace std;

void main( ) {

       vector<int> vectInt;

       int i;

       //     初始化 vector 容器

       for (i = 0; i < 5; i++ ) {

              vectInt.push_back( i );

       }

       //     以下代碼是要?jiǎng)h除所有值為 4 的元素

       vector<int>::iterator itVect = vectInt.begin();

       for ( ; itVect != vectInt.end();  ++itVect ) {

              if ( *itVect == 4 ) {

                     vectInt.erase( itVect );

              }

       }

       int iSize = vectInt.size();

      for (  i = 0 ; i < iSize; i++ )  {

                     cout << " i= " << i <<  ", " << vectInt[ i ] << endl;

       }

 

}

例 2 :

#include <iostream>

#include <vector>

using namespace std;

void main( ) {

       vector<int> vectInt;

       int i;

       //     初始化 vector 容器

       for ( i = 0; i < 5; i++ ) {

              vectInt.push_back( i );

              if ( 3 == i ) {

                     //       使 3 的元素有兩個(gè),并且相臨。這非常關(guān)鍵,否則將發(fā)現(xiàn)不了 bug 。

                     //   具體解釋見下。

                     vectInt.push_back( i );

              }

       }

       vector<int>::iterator itVect = vectInt.begin();

       vector<int>::iterator itVectEnd = vectInt.end(); //       防止 for 多重計(jì)算

       //     以下代碼是要?jiǎng)h除所有值為 3 的元素

       for ( ; itVect != itVectEnd; ++itVect ) {

              if ( *itVect == 3 ) {

                     itVect = vectInt.erase( itVect );

              }

       }

       int iSize = vectInt.size();

      for (  i = 0 ; i < iSize; i++ )  {

                     cout << " i= " << i <<  ", " << vectInt[ i ] << endl;

       }

例 3 :

#include <iostream>

#include <vector>

using namespace std;

void main( ) {

       vector<int> vectInt( 5 );

       int i;

       vectInt[ 0 ] = 0;

       vectInt[ 1 ] = 1;

       vectInt[ 2 ] = 2;

       vectInt[ 3 ] = 3;

       vectInt[ 4 ] = 4; //     替換為 vectInt[ 4 ] = 3; 試試

       vector<int>::iterator itVect = vectInt.begin();

       vector<int>::iterator itVectEnd = vectInt.end(); //       防止 for 多重計(jì)算

       //     以下代碼是要?jiǎng)h除所有值為 3 的元素

       for ( ; itVect != itVectEnd; ) {

              if ( *itVect == 3 ) {

                     itVect = vectInt.erase( itVect );

              }

              else {

                     ++itVect;

              }

       }

       int iSize = vectInt.size();

      for (  i = 0 ; i < iSize; i++ )  {

                     cout << " i= " << i <<  ", " << vectInt[ i ] << endl;

       }

}

 

分析:

這里最重要的是要理解 erase 成員函數(shù),它刪除了 itVect 迭代器指向的元素,并且返回要被刪除的 itVect 之后的迭代器, 迭代器相當(dāng)于一個(gè)智能指針,指向容器中的元素,現(xiàn)在刪除了這個(gè)元素,將導(dǎo)致內(nèi)存重新分配,相應(yīng)指向這個(gè)元素的迭代器之后的迭代器 就失效了,但 erase 成員函數(shù)返回要被刪除的 itVect 之后的迭代器 。

例 1 將導(dǎo)致程序未定義的錯(cuò)誤,在 windows 中即是訪問非法內(nèi)存,程序當(dāng)?shù)?。因?yàn)?vectInt.erase( itVect ); 調(diào)用后 itVect 之后的迭代器已無效了,所以當(dāng)執(zhí)行 ++itVect 后, *itVect 訪問了非法內(nèi)存。例 1 也是初學(xué)者最容易犯的錯(cuò)誤,這個(gè)錯(cuò)誤也比較容易發(fā)現(xiàn)。

例 2 可能會(huì)導(dǎo)致不能把 vectInt 中所有為 3 的元素刪除掉。因?yàn)榈谝淮蝿h除成功時(shí), itVect = vectInt.erase( itVect );itVect 為指向 3 之后的位置,之后再執(zhí)行 ++itVect , itVect 就掉過了被刪除元素 3 之后的元素 3 ,導(dǎo)致只刪除了一個(gè)為 3 的元素,這個(gè) bug 比較隱蔽,因?yàn)槿绻皇莾蓚€(gè)均為 3 的元素相臨,就將很難捕捉到這個(gè) bug ,程序有可能在一段時(shí)間運(yùn)行良好,但如碰到容器中兩值相同的元素相臨,則程序就要出問題。

例 3 ,對(duì)于本例你可能要說程序沒有任何問題,解決了上面的兩個(gè) bug ,程序也運(yùn)行正常。但且慢,你把 “ vectInt[ 4 ] = 4; ” 這一行改為 “ vectInt[ 4 ] = 3; ”試試,一運(yùn)行,程序當(dāng)?shù)?,訪問非法內(nèi)存!你疑惑不解:從程序看不出 bug ,而且我還把 vectInt.end() 放在外面計(jì)算以防止 for 多重計(jì)算,提高效率。哈哈,問題就出在最后一句話!算法大師 Donald Knuth 有一句名言:不成熟的優(yōu)化是一切惡果的根源( Permature optimization is the root of all evil )。由于在 for 循環(huán)中要?jiǎng)h除元素,則 vectInt.end() 是會(huì)變化的,所以不能在 for 循環(huán)外計(jì)算,而是每刪除一次都要重新計(jì)算,所以應(yīng)放在 for 循環(huán)內(nèi)。那你要問,為什么把 “ vectInt[ 4 ] = 4; ” 這一行改為 “ vectInt[ 4 ] = 3; ”程序就會(huì)當(dāng)?shù)?,而不改程序就很正常呢?這就跟 vector 的實(shí)現(xiàn)機(jī)制有關(guān)了。下面以圖例詳細(xì)解釋。

vectInt 的初始狀態(tài)為:

 

                                                                             | end

0        1        2        3         4     

                                     

刪除 3 后,

 

                                                                 | 新的 end  | 原來的 end

0         1        2        4         4     

 

 

 

注意上面“新的 end ”指向的內(nèi)存并沒有被清除,為了效率, vector 會(huì)申請(qǐng)超過需要的內(nèi)存保存數(shù)據(jù),刪除數(shù)據(jù)時(shí)也不會(huì)把多余的內(nèi)存刪除。

然后 itVect 再執(zhí)行 ++itVect ,因?yàn)榇藭r(shí) *itVect 等于 4 ,所以繼續(xù)循環(huán), 這時(shí) itVect 等于“新的 end ”但不等于“原來的 end ”(它即為 itVectEnd ),所以繼續(xù),因?yàn)?*itVect 訪問的是只讀內(nèi)存得到的值為 4 ,不等于 3 ,故不刪除,然后執(zhí)行 ++itVect 此時(shí) itVect 等于 itVectEnd 退出循環(huán)。從上面過程可以看出,程序多循環(huán)了一次(刪除幾次,就要多循環(huán)幾次),但程序正常運(yùn)行。

如果把 “ vectInt [ 4 ] = 4; ” 這一行改為 “ vectInt [ 4 ] = 3; ”過程如下:

 

 

                                                                        | end

0        1        2        3         3     

 

刪除 3 后,

 

                                                                 | 新的 end       | 原來的 end

0        1        2        3         3      

 

 

刪除第 2 個(gè) 3 后,

 

                                                 | 新的 end            | 原來的 end

0        1        2        3         3     

 

這時(shí) itVect 等于“新的 end ”但不等于“原來的 end ”(它即為 itVectEnd ),所以繼續(xù),因?yàn)?*itVect 訪問的是只讀內(nèi)存得到的值為 3 ,等于 3 ,所以執(zhí)行刪除,但因?yàn)?*itVect 訪問的是只讀內(nèi)存不能刪除,所以程序當(dāng)?shù)簟?

綜上,我們知道當(dāng)要?jiǎng)h除的值在容器末尾時(shí),會(huì)導(dǎo)致程序刪除非法內(nèi)存,程序當(dāng)?shù)?;即使程序正常運(yùn)行,也是 for 循環(huán)多執(zhí)行了等于刪除個(gè)數(shù)的循環(huán)。所以把 vectInt.end() 放在 for 循環(huán)外面執(zhí)行,完全是錯(cuò)誤的。對(duì)于 list 容器, list.end() 在刪除過程中是不會(huì)變的,可以把它放在 for 循環(huán)外面計(jì)算,但由于 list.end() 是個(gè)常量,把 list.end() 放在 for 循環(huán)中計(jì)算編譯器應(yīng)該可以優(yōu)化它。從安全考慮,除非你能保證 for 循環(huán)中不會(huì)改變?nèi)萜鞯拇笮?,否則都應(yīng)該對(duì)容器的值在 for 循環(huán)中計(jì)算,對(duì)于 vectInt.size() 這樣的計(jì)算,也應(yīng)該在 for 循環(huán)中計(jì)算,不要因?yàn)槲⑿〉膬?yōu)化而導(dǎo)致程序出錯(cuò)。

 

正確的方法:

例 4 :

#include <iostream>

#include <vector>

using namespace std;

void main( ) {

       vector<int> vectInt;

       int i;

       for (  i = 0; i < 5; i++ ) {

              vectInt.push_back( i );

              if ( 3 == i ) {

                     //       使 3 的元素有兩個(gè),并且相臨。

                     vectInt.push_back( i );

              }

       }

       vector<int>::iterator itVect = vectInt.begin();

       //     以下代碼是要?jiǎng)h除所有值為 3 的元素

       for ( ; itVect != vectInt.end();  ) {  // 刪除 ++itVect{

              if ( *itVect == 3 ) {

                     itVect = vectInt.erase( itVect );

              }

              else {

                     ++itVect;

              }

       }

       //     把 vectInt.size() 放在 for 循環(huán)中

      for (  i = 0 ; i < vectInt.size(); i++ )  {

                     cout << " i= " << i <<  ", " << vectInt[ i ] << endl;

       }

運(yùn)行結(jié)果為:

i= 0, 0

i= 1, 1

i= 2, 2

i= 3, 4

從結(jié)果顯示值為 3 的元素確實(shí)被刪除了。


本文來自CSDN博客,轉(zhuǎn)載請(qǐng)標(biāo)明出處:http://blog.csdn.net/OExpress/archive/2006/06/15/799572.aspx

本站僅提供存儲(chǔ)服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊舉報(bào)。
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
什么是STL
STL之vector的使用
STL模板總結(jié)
STL學(xué)習(xí)總結(jié)
stl容器學(xué)習(xí)總結(jié)
C++基礎(chǔ)相關(guān)STL::List 用法簡單介紹
更多類似文章 >>
生活服務(wù)
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號(hào)成功
后續(xù)可登錄賬號(hào)暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點(diǎn)擊這里聯(lián)系客服!

聯(lián)系客服