2012-03-06 23 views
0

this question的幫助之後,我使用了對我的類「移動器」的引用來處理矢量中的對象(作爲集合的一部分)。然而,我遇到了問題,我似乎無法確定是什麼原因造成的。看來,一旦我達到了我的向量中的30-35個對象(以僞隨機間隔添加),程序就會停止。沒有崩潰,只是暫停,我必須手動結束任務(CTRL-C不起作用)。引用對象的C++錯誤 - 如何調試?

我的問題似乎在於這些代碼位。我原來:

int main() { 
std::vector<Mover> allMovers; 
std::vector<Mover>::iterator iter = allMovers.begin(); 
//This code runs to the end, but the 'do stuff' lines don't actually do anything. 
Mover tempMover; 
//Other code 
while(iter < allMovers.end()) { 
tempMover = *iter; 
//Do stuff with tempMover 
//Add another tempMover at a random interval 
allMovers.push_back(CreateNewMover()); 
iter++; 
} 
//Other code 
} 

我的更新對前一個問題鏈接到上面後:

int main() { 
std::vector<Mover> allMovers; 
std::vector<Mover>::iterator iter = allMovers.begin(); 
//This code crashes once about 30 or so items exist in the vector, but the 'do stuff' lines do work. 
//Other code 
while(iter < allMovers.end()) { 
Mover& tempMover = *iter; 
//Do stuff with tempMover 
//Add another tempMover at a random interval 
allMovers.push_back(CreateNewMover()); //Crashes here. 
iter++; 
} 
//Other code 
} 

如何跟蹤下來的任何想法?我有std :: couts遍佈這個地方來標記代碼對我來說。崩潰(雖然發生在不同數量的對象上)始終在push_back()上崩潰,儘管在崩潰之前在同一次運行中多次成功地工作。

編輯

雖然我接受和(認爲)我明白了答案回覆:迭代器,我不明白的是爲什麼代碼DOES工作完全當我不使用的參考目的? (第一個代碼塊)。

另一個編輯 如果有人正在尋找這個具體問題,我的問題的一部分沒有解決:「如何調試?」作爲一名C++新手,我不知道gdb調試器(使用MinGW)。現在我已經知道了它,它在找到這些問題的根源方面非常有幫助。

回答

4

當一個向量重新分配其內存時,所有迭代器都將失效(以及任何引用或指向任何元素的指針)。所以有時你的push_back將使iter無效,並試圖在事後使用它給出未定義的行爲。

最簡單的解決方法是使用索引而不是迭代器。或者,如果您可以計算矢量的最大大小的上限,則可以在循環之前調用reserve以確保它永不重新分配。或者你可以使用std::list,當插入新元素時,其迭代器被保留。

更新:關於您的編輯,都給出未定義的行爲。這可能是,在第一種情況下,你不會崩潰,因爲你不訪問一個懸而未決的參考(在第二次訪問tempMover時可能會非常好地崩潰),然後內存碰巧重新分配在一個較低的地址之前,所以while條件(使用<而不是更傳統的!=)立即退出循環。或者可能會發生完全不同的事情 - 這是未定義行爲的本質。

+0

你是對的,我完全錯過了。 +1 – 2012-03-06 12:51:56

+0

我以前試過索引。但是,當我這樣做時,我遇到了增加循環最大值的問題。當我寫這篇文章時,我可以看到如何避免這種情況,所以我想我會回到那種方法。謝謝! – Gaffi 2012-03-06 13:10:14

+1

@Gaffi:你可以在'while'條件下使用'allMovers.size()'來確保它始終是最新的。 – 2012-03-06 13:29:06

1

你也許可能需要更改包含while語句行:

while(iter != allMovers.end()) { 

<運營商似乎很好地工作通常是矢量,但我使用!=與其他容器,也工作了較好的效果似乎在更多的示例代碼中使用。


更新

您可以用等同for循環這樣的替換while循環:

for(std::vector<Mover>::iterator iter = allMovers.begin(); iter != allMovers.end(); ++iter) 
{ 

這具有迭代器iter的增量「有它的位置優勢「並且不太可能被遺忘。


更新2

如果我理解你上面的例子,你想用一些內容來填充容器。我建議(與其他人一樣)徹底擺脫迭代器。

int main() 
{ 
    std::vector<Mover> allMovers; 

    //Other code 

    while(1) // this loop will add new movers as long as it succeeds to create one 
    { 
     Mover new_mover = CreateNewMover(); 
     if (IS EMPTY (new_mover)) // pseudocode. Check if the previous 
      break;     // CreateNewMover() succeeded. 

     allMovers.push_back(new_mover); 
    } 

    //Other code 
} 
+0

我以前見過這個想法,我可能應該切換。不過,考慮到其他答案,它看起來對我來說是更好的解決方案,它完全避免使用迭代器。謝謝! – Gaffi 2012-03-06 13:11:25

+0

對,我確實想改變向量,所以迭代器不是正確的選項。我已經改變了代碼來使用索引,它效果很好! – Gaffi 2012-03-07 10:38:18

1

你是(可能)做錯了。

問題是,在容器上混合迭代並對容器結構進行操作(這裏添加對象)極易出錯。

每當您在allMovers中添加元素時,就有可能導致iter無效。在iter失效後的任何用法是未定義的行爲

它可以正確地做到這一點:

iter = allMovers.insert(allMovers.end(), CreateNewMover()); 

但它只是一般一個壞主意。

我的建議是禁止這種代碼完全來自您的代碼庫。每一次事件都是一個錯誤。找到另一種算法。

+0

雖然這基本上是我接受的答案。 @Mike Seymour提供了使用索引的一點額外功能。不過謝謝! – Gaffi 2012-03-06 13:13:18

+1

'insert'將返回一個指向插入元素的迭代器,這不是在這裏想要的。我認爲保留'iter'的唯一方法是在插入之前將其轉換爲索引;在這種情況下,你可能會使用索引(或不同的容器)。 – 2012-03-06 13:20:17

+1

@MikeSeymour:我不知道用例是什麼,因爲所有代碼都隱藏在問題中:)但我同意在這種情況下索引會更好地工作(或使用'deque'來避免重定位),但它特別是這個用例,我更喜歡避免這種容易出錯的結構。 – 2012-03-06 13:28:22

1

從文檔push_back()

如果新的大小()不大於容量(大),沒有迭代器或引用無效。否則,所有迭代器和引用都將失效。

當你達到30個或某些對象new size() > capacity(),導致迭代器iter,這是derefenced導致未定義的行爲無效。