iterator中文經常被譯為“泛型指標”,在實際的使用中卻需要比指標更加小心翼翼。如果沒有注意到iterator失效,那麼產生的錯誤可能比普通指標更隱蔽,當然也可能會引起一般的程式異常。iterator失效主要有兩種情況:
1、iterator變數已經變成了“懸null 指標”,對它進行*,++,--都會引起程式記憶體操作異常;
2、iterator所指向的變數已經不是你所以為的那個變數了。
所以在使用iterator是必須要時刻牢記“容器的插入和刪除元素”可能引起iterator失效!普遍遇到的幾個問題:
1、剛剛使用STL的很多人會為這個問題困擾:在迴圈遍曆一個容器時,需要根據條件刪除其中的某個元素,如何處理iterator?答案是:對於序列式容器標準寫法是這樣:
//vector<int> con;
for(vector<int>::iterator iter=con.begin();
iter!=con.end();)
{
if((*iter) == 99)
iter=con.erase(iter);
else
++iter;
}
對於關聯容器是這樣:
//map<int,int> con;
for(map<int,int>::iterator iter=con.begin();
iter!=con.end();)
{
if(iter->second == 99)
con.erase(iter++);
else
++iter;
}
2、使用一個iterator變數時往往是危機重重,我在剛開始使用STL的時候,就範了一個較難測試出來的錯誤,類似這樣:
int DoSomething(vector<int>& con)
{
vector<int>::iterator iter = find(con.begin(),con.end(),99);
... ...//做了一些insert操作
return *iter;//如果vector的記憶體沒有重新分配,那麼*操作會成功,但是已經不保證是99了!
}
3、下面這個錯誤則要更隱蔽一些、在大一些的類庫中更難以控制:
有一個介面系統,一個window類,用來封裝自身的操作、繪圖功能,並管理它的所有子視窗:
class Window
{
Window* m_pParent;
list<Window*> m_children;
public:
void RemoveChild(Window *pChild)
{
m_children.remove(pChild);
}
virtual void Update()
{
for(list<Window*>::iter=m_children.begin();
iter=m_children.end();++iter)
{
(*iter)->Update();
}//endof for
}
};
這個Window::Update()函數負責處理視窗邏輯,結果在某個視窗的Update寫成了這樣:
class MyDlg : public Window
{
public:
virtual void Update()
{
if(m_bClosed)
m_pParent->RemoveChild(this);
else
Window::Update();
}
};
當一個模組的邏輯比上述例子複雜很多時,這種情況更難以掌握,為了防止模組客戶的代碼產生類似的行為,我不得不把代碼寫成這樣:
class Window
{
Window* m_pParent;
list<Window*> m_children;
bool m_bLock;
public:
void RemoveChild(Window *pChild)
{
ASSERT(!m_bLock);
m_children.remove(pChild);
}
virtual void Update()
{
m_bLock=true;
for(list<Window*>::iter=m_children.begin();
iter=m_children.end();++iter)
{
(*iter)->Update();
}//endof for
m_bLock=false;
}
};
參考資料:
《C++標準程式庫》,候捷、孟岩譯,2002