問題:
有一個stl容器, 有一個已經獲取到的容器的迭代器, 當向stl容器中新增元素或刪除元素時, 先前拿到的迭代器還有用嗎? 也就是迭代器是否失效了?
迭代器失效,有兩個層面的意思,
1) 無法通過迭代器++,--操作遍歷整個stl容器. 記作: 第一層失效
2) 無法通過迭代器存取迭代器所指向的內存. 記作: 第二層失效
關於這個問題, 不同的容器對應的結果是不同的. 我們分別分析.
vector
vector是個連續內存存儲的容器. 如果vector容器的中間某個元素被刪除或從中間插入一個元素. 有可能導致內存空間不夠用而重新分配一塊大的內存.
這個動作將導致先前獲取的迭代器, 第一層和第二層均失效.
map
map內部是紅黑樹結構, 當map中新插入或刪除元素後, 樹的結構會相應的調整.
但是, 通過先前的迭代器, 仍然可以通過++可以遍歷map. 能遍歷到值>當前迭代器的節點. (沒有看具體stl內部實現,初步代碼驗證正確)
迭代器指向的用戶數據內存始終有效.
list
同理map, 通過++只能遍歷鏈表右側的節點. 迭代器指向的用戶數據內存也始終有效.
後記:爲什麼會有這篇文章?
是因爲最近在做一個LRU cache系統, 希望利用stl提供的hash_map和list來實現, 中間涉及到迭代器的是否失效問題.
特別注意,
1) 這裏的結論僅使用於單線程的stl編程. 如果是多線程的, 就不是簡單的迭代器失效問題了, 直接core了. stl是非線程安全的.
2) 這裏討論的先前獲取的迭代器,一定是沒有被erase的, 如果一旦erase, 那麼迭代器肯定失效. 注意, erase其他迭代器不影響先前獲取的迭代器.
map的測試代碼:
#include <stdio.h>
#include <stdlib.h>
#include <string>
#include <arpa/inet.h>
#include <sstream>
#include <vector>
#include <map>
#include <list>
#include <unistd.h> // for usleep
using namespace std;
#ifndef foreach
#define foreach(container,it) \
for(typeof((container).begin()) it = (container).begin();it!=(container).end();++it)
#endif
int main()
{
map<int , int> im;
im[1] = 1;
im[10] = 10;
im[20] = 20;
im[21] = 21;
map<int, int>::iterator it;
map<int, int>::iterator it1;
map<int, int>::iterator it2;
map<int, int>::iterator it3;
it1 = im.find(1);
it2 = im.find(10);
it3 = im.find(20);
//--------------------------------
// 邊遍歷便插入測試
//--------------------------------
int i = 30;
foreach(im,it)
{
printf("it->first:%d, it->second:%d\n", it->first, it->second);
if (i<40)
{
im[++i] = i;
im[-i] = -i;
}
}
printf("-----------------\n");
//--------------------------------
// 遍歷完成後,拿着以前的iterator測試
//--------------------------------
for(it=it3; it!=im.end(); ++it)
{
printf("it->first:%d, it->second:%d\n", it->first, it->second);
}
return 0;
}
輸出:
it->first:1, it->second:1
it->first:10, it->second:10
it->first:20, it->second:20
it->first:21, it->second:21
it->first:31, it->second:31
it->first:32, it->second:32
it->first:33, it->second:33
it->first:34, it->second:34
it->first:35, it->second:35
it->first:36, it->second:36
it->first:37, it->second:37
it->first:38, it->second:38
it->first:39, it->second:39
it->first:40, it->second:40
-----------------
it->first:20, it->second:20
it->first:21, it->second:21
it->first:31, it->second:31
it->first:32, it->second:32
it->first:33, it->second:33
it->first:34, it->second:34
it->first:35, it->second:35
it->first:36, it->second:36
it->first:37, it->second:37
it->first:38, it->second:38
it->first:39, it->second:39
it->first:40, it->second:40