STL 迭代器失效問題

轉載地址:http://www.cnblogs.com/xkfz007/articles/2509433.html

最近在項目開發中,遇到一個異常,經過測試,發現是迭代器失效問題,於是稍微總結了一下。

vector迭代器失效測試:

測試程序: 

複製代碼
void vectorTest()
{
    vector<int> container;
    for (int i = 0; i < 10; i++)
    {
        container.push_back(i);
    }

    vector<int>::iterator iter;
     for (iter = container.begin(); iter != container.end(); iter++)
    {
            if (*iter > 3)
              container.erase(iter);
    }

     for (iter = container.begin(); iter != container.end(); iter++)
    {
            cout<<*iter<<endl;
    }
}
複製代碼
測試結果如下:


結果對嗎?肯定不對,結果應該是0,1,2,3。

在看看跟進去之後的報錯情況:

迭代器在執行++操作時報錯!

對於序列式容器,比如vector,刪除當前的iterator會使後面所有元素的iterator都失效。這是因爲順序容器內存是連續分配,刪除 一個元素導致後面所有的元素會向前移動一個位置。但是erase方法可以返回下一個有效的iterator。所以代碼做如下修改,就OK了。

複製代碼
void vectorTest()
{
    vector<int> container;
    for (int i = 0; i < 10; i++)
    {
        container.push_back(i);
    }

    vector<int>::iterator iter;
    for (iter = container.begin(); iter != container.end(); )
    {
            if (*iter > 3)
              iter = container.erase(iter);    //erase的返回值是刪除元素下一個元素的迭代器
            else{
                iter++;
            }
    }

     for (iter = container.begin(); iter != container.end(); iter++)
    {
            cout<<*iter<<endl;
    }
}
複製代碼

運行結果如下:

結果是正確的。

 

再看一個MAP的示例,

複製代碼
void mapTest()
{
    map<intstring> dataMap;


    for (int i = 0; i < 100; i++)
    {
           string strValue = "Hello, World";

            stringstream ss;
            ss<<i;
            string tmpStrCount;
            ss>>tmpStrCount;
            strValue += tmpStrCount;
            dataMap.insert(make_pair(i, strValue));
    }

    cout<<"MAP元素內容爲:"<<endl;
     map<intstring>::iterator iter;
    for (iter = dataMap.begin(); iter != dataMap.end(); iter++)
    {
            int nKey = iter->first;
            string strValue = iter->second;
            cout<<strValue<<endl;
    }

    cout<<"內容開始刪除:"<<endl;
    /////////////////////////////////////////////擦除操作引發迭代器失效
    for (iter = dataMap.begin(); iter != dataMap.end(); iter++)
    {
            int nKey = iter->first;
            string strValue = iter->second;

           if (nKey % 2 == 0)
           {
                dataMap.erase(iter);

           }
            cout<<iter->second<<endl;
    }
}
複製代碼

結果報錯:

看看報異常時跟進去在哪裏報錯:

根據迭代器訪問數據時失效! map/set iterator not dereferencable

迭代器是不引用的。

正確的做法是: 

複製代碼
void mapTest()
{
    map<intstring> dataMap;


    for (int i = 0; i < 100; i++)
    {
          string strValue = "Hello, World";

          stringstream ss;
          ss<<i;
          string tmpStrCount;
          ss>>tmpStrCount;
          strValue += tmpStrCount;
          dataMap.insert(make_pair(i, strValue));
    }

    cout<<"MAP元素內容爲:"<<endl;
     map<intstring>::iterator iter;
    for (iter = dataMap.begin(); iter != dataMap.end(); iter++)
    {
          int nKey = iter->first;
          string strValue = iter->second;
          cout<<strValue<<endl;
    }

    cout<<"內容開始刪除:"<<endl;
    for (iter = dataMap.begin(); iter != dataMap.end(); )
    {
         int nKey = iter->first;
         string strValue = iter->second;

         if (nKey % 2 == 0)
         {
             dataMap.erase(iter++);

         }else
     {
          iter++;
         }

         if (iter != dataMap.end())
         cout<<iter->second<<endl;
    }
複製代碼

最後輸出結果:

程序在一邊刪除一遍輸出,而且能夠順利輸出結束。

總結一下:

vector是一個順序容器,在內存中是一塊連續的內存,當刪除一個元素後,內存中的數據會發生移動,以保證數據的緊湊。所以刪除一個數據後,其他數據的地址發生了變化,之前獲取的迭代器根據原有的信息就訪問不到正確的數據。

所以爲了防止vector迭代器失效,常用如下方法:

複製代碼
for (iter = container.begin(); iter != container.end(); )
    {
            if (*iter > 3)
              iter = container.erase(iter);    //erase的返回值是刪除元素下一個元素的迭代器
            else{
                iter++;
            }
    }
複製代碼

這樣刪除後iter指向的元素後,返回的是下一個元素的迭代器,這個迭代器是vector內存調整過後新的有效的迭代器。萬無一失!

 

map是關聯容器,以紅黑樹或者平衡二叉樹組織數據,雖然刪除了一個元素,整棵樹也會調整,以符合紅黑樹或者二叉樹的規範,但是單個節點在內存中的地址沒有變化,變化的是各節點之間的指向關係。

所以在map中爲了防止迭代器失效,在有刪除操作時,常用如下方法:

複製代碼
for (iter = dataMap.begin(); iter != dataMap.end(); )
    {
         int nKey = iter->first;
         string strValue = iter->second;

         if (nKey % 2 == 0)
         {
               map<intstring>::iterator tmpIter = iter;
           iter++;
               dataMap.erase(tmpIter);
               //dataMap.erase(iter++) 這樣也行

         }else
     {
          iter++;
         }
   }
複製代碼

 其中,

map<int, string>::iterator tmpIter = iter; iter++;

dataMap.erase(tmpIter);

這幾句的意思是,先保留要刪除的節點迭代器,再讓iter向下一個有意義的節點,然後刪除節點

所以這個操作結束後iter指向的是下一個有意義的節點,沒有失效。

其實這三句話可以用在一句話代替,就是dataMap.erase(iter++);

解釋是先讓iter指向下一個有效的節點,但是返回給erase函數的是原來的iter副本。這個可能跟++這個操作的本身語法相關。

但是功能跟上面是一樣的。

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