程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> 關於C++ >> 淺談c++ stl迭代器生效的問題

淺談c++ stl迭代器生效的問題

編輯:關於C++

淺談c++ stl迭代器生效的問題。本站提示廣大學習愛好者:(淺談c++ stl迭代器生效的問題)文章只能為提供參考,不一定能成為您想要的結果。以下是淺談c++ stl迭代器生效的問題正文


之前看《C++ Primier》的時分,也解到在順序型窗口裡insert/erase會觸及到迭代器生效的問題,並沒有深究。明天寫順序的時分遇到了這個問題。

1 莫明其妙的Erase

最初我的順序是醬紫的,別說話,我知道這樣是有問題的,可這樣是最直觀的想法

int arr[]={0,1,2,3,4,5,6,7,8,9,10}; 
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));for (auto it = a.begin(); it != a.end();++it ){
    if ((*it)&1){
      a.erase(it);
    }
  }  

沒錯,順序解體!刪除了迭代器it之後,it迭代器生效了,無法再停止++it操作了。

可是,當我覺得erase做的只是把it之後的元素向前挪動一個地位而已,為什麼迭代器生效了呢?我翻開《STL源碼分析》,SGI STL的vector<T,Alloc>::erase的源碼是這樣的:

iterator vector<T, Alloc>::erase(iterator position)
  {
    if (position + 1 != end())
      copy(position + 1, finish, position);
    --finish;
    destroy(finish);
    return position;
  }

 正如我所想,erase函數並沒有對輸出的position迭代器停止改寫!我打印出調試信息,發現erase之後,迭代器的_Ptr成員,也就是指針的值並沒有發作變化,而此指針所指的元素確實是下一個元素。那麼為什麼生效了呢?

我又查了《C++ Primier》,發現此書上的規范寫法是這樣的:

int arr[]={0,1,2,3,4,5,6,7,8,9,10}; 
  vector<int> a(arr,arr+sizeof(arr)/sizeof(*arr));
  for (auto it = a.begin(); it != a.end();){
    if ((*it)&1){
      it=a.erase(it);
    }
    else
      ++it;  
  }

運轉了一下,這樣是沒錯的。我打印了調試信息,發現與之前一樣,erase之後把後果賦給it,it裡的成員_Ptr並沒有發作變化。獨一的能夠就是迭代器裡還有別的標志,假如以後元素被刪除之後,該迭代器也就“生效”了。《C++ Primier》並未對此作出過多解釋,只是說,erase函數前往被刪除元素的下一個元素的迭代器。

結論:在STL裡,我們不能以指針來對待迭代器,指針是與內存綁定的,而迭代器是與容器裡的元素綁定的,刪除了之後,該迭代器就生效了,在對其重新賦值之前,不能再訪問此迭代器。

2 愈加小心冀冀地Insert

機智如我,自然會去探究一下insert之後,迭代器會怎樣。於是: 

vector<int> a;
  for (int i = 0; i < 10; ++i)
  {
    a.push_back(i);
  }

  for (auto it = a.begin(); it != a.end(); ++it){
    if (*it == 5){ 
      a.insert(it, 100);
       ++it;
    }
  }

你猜怎樣著??

啥事兒沒有!你能夠會問,拔出之後為什麼要++it。拔出之前,it指向5,在5之前拔出100後,it指向100。這樣下一次循環,it仍然會指向5。置信我,你的順序會爆炸的!

我作了個++it之後,it又指向5,下一次循環就直接指向5之後的元素了,順利完成拔出任務。

世界戰爭~世界戰爭~我還真不確定。

忽然想到,當拔出元素過多,vector的capacity會添加,這時會不會問題呢?說干就干:

vector<int> a;
  for (int i = 0; i < 13; ++i)
  {
    a.push_back(i);
  }

  for (auto it = a.begin(); it != a.end(); ++it){
    if (*it == 5){ 
      a.insert(it, 100);
       ++it;
    }
  }

BOOM!果真解體了!也就是說拔出之後的迭代器生效了。那之前的呢?

我決議粗犷地測試一下:

vector<int> a;
  for (int i = 0; i < 13; ++i)
  {
    a.push_back(i);
  }
  auto it1=a.begin();
  for (auto it = it1; it != a.end(); ++it){
    if (*it == 5){ 
      a.insert(it, 100);
       it=it1;
    }
  }

我拔出之後,直接讓it指向begin(),然後單步伐試。執行完it=it1還好好的,可再去執行++it還是解體了。

也就是說,capacity變化之後,一切的迭代器都生效了!這是當然了呀!capacity發作變化,容器外部做的不只僅是添加capacity這麼復雜,由於容器所在內存前面能夠沒有足夠的內存讓我們運用,所以,容器要重新開拓一段足夠大的內存來存儲容器裡的元素,以後內存會被釋放。這樣一來,迭代器自然生效了。

3 C++ Primier的總結

關於容器的迭代器生效的問題,C++ Primier用了一大節作了總結,我翻譯成中文如下:

(1)添加元素到容器後

關於vector和string,假如容器內存被重新分配,iterators,pointers,references生效;假如沒有重新分配,那麼拔出點之前的iterator無效,拔出點之後的iterator生效;

關於deque,假如拔出點位於除front和back的其它地位,iterators,pointers,references生效;當我們拔出元素到front和back時,deque的迭代器生效,但reference和pointers無效;

關於list和forward_list,一切的iterator,pointer和refercnce無效。

(2)沉著器中移除元素後

關於vector和string,拔出點之前的iterators,pointers,references無效;off-the-end迭代器總是生效的;

關於deque,假如拔出點位於除front和back的其它地位,iterators,pointers,references生效;當我們拔出元素到front和back時,off-the-end生效,其他的iterators,pointers,references無效;

關於list和forward_list,一切的iterator,pointer和refercnce無效。

(3)在循環中refresh迭代器

當處置vector,string,deque時,當在一個循環中能夠添加或移除元素時,要思索到迭代器能夠會生效的問題。我們一定要refresh迭代器。

int arr[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10 };
  deque<int> v(arr,arr+sizeof(arr)/sizeof(*arr));
  for (auto it = v.begin(); it != v.end(); )
  {
    if ((*it) & 1)
    {
      it = v.insert(it, *it);
      it += 2;
    }
    else
      it = v.erase(it);
  }

至於it+=2,很容易解釋,insert之後,it指向新添加的元素,+2之後,it指向下一個要處置的元素。

(4)在循環不變式中不要store off-the-end迭代器

這個很容易了解了,添加或移除元素之後,off-the-end生效了,不store的話,每次從end()函數中取的都是最新的off-the-end,自然不會生效。

最後:《C++ Primier》是本好書。

以上就是為大家帶來的淺談c++ stl迭代器生效的問題全部內容了,希望大家多多支持~

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved