2014-09-24 74 views
0

我認爲我有一些誤解如何通過值與參考值來移動STL容器 對象。具體地講,我不明白爲什麼 下面的程序崩潰:STL容器對象按值與參考

#include <vector> 
#include <set> 
#include <cstdio> 

class Value { 
public: 
    int x, y; 
    Value(int a, int b) { x = a; y = b; } 
}; 

class Test { 
public: 
    Test(int x, int y) { values.insert(new Value(x, y)); } 
    void add(int x, int y) { values.insert(new Value(x, y)); } 
    std::set<Value *> getValues() { return values; } 
private: 
    std::set<Value *> values; 
}; 

int main() { 
    std::vector<Test> Ts; 
    for (unsigned i = 0; i < 5; i++) { 
    Test t(0, 0); 
    t.add(i, 0); 
    Ts.push_back(t); 
    } 
    for (unsigned i = 0; i < 5; i++) { 
    for (std::set<Value *>::iterator it = Ts.at(i).getValues().begin(), ite = Ts.at(i).getValues().end(); it != ite; ++it) { 
     Value *v = *it; 
     printf("(%d, %d) ", v->x, v->y); 
    } 
    printf("\n"); 
    } 
    return 0; 
} 

在第二個for循環此程序段錯誤(試圖在Ts矢量打印 值)。但是,如果將初始循環更改爲:

for (unsigned i = 0; i < 5; i++) { 
    Ts.push_back(Test(0, 0)); 
} 

然後程序執行正常。此外,如果你拿第一 程序(崩潰)和打印循環更改爲:

for (unsigned i = 0; i < 5; i++) { 
    std::set<Value *> values = Ts.at(i).getValues(); 
    for (std::set<Value *>::iterator it = values.begin(), ite = values.end(); it != ite; ++it) { 
    Value *v = *it; 
    printf("(%d, %d) ", v->x, v->y); 
    } 
    printf("\n"); 
} 

然後該程序不會崩潰。

我想了解是什麼導致這些崩潰,程序之間的差異是什麼。

std::set<Value *> getValues() { return values; } 

該成員函數返回的指針集的副本:

+0

您是否知道您的程序正在泄漏內存?在'Test'類中使用'new'完全沒有理由。 – pmr 2014-09-24 16:04:53

+0

@pmr是的。這是我的大型程序的簡化測試用例版本,它稍後可以正確釋放這些值。 – 2014-09-24 16:06:24

+1

除了你正在泄漏內存的部分外,你還在for循環中設置了'values'的多個副本,因爲'getValues()'返回一個副本。這可能是迭代器在內存中處於不利位置的原因,導致分段錯誤。 – 2014-09-24 16:06:41

回答

2

,我似乎找到這裏的主要問題是在兩行代碼中總結出來的。

for (std::set<Value *>::iterator it = Ts.at(i).getValues().begin(), ite = Ts.at(i).getValues().end(); it != ite; ++it) 

使用相同的邏輯行,該指令在初始化階段創建兩組。 ite不會是預期集合的結束迭代器,而是另一個新創建的容器的結束指針。其結果是,在it指向內存中的其他意外位置之前很可能不會達到it != ite

你的改正是有效的,因爲你現在總是處理來自同一組的迭代器。一個副本仍然在這裏發生,但在這種情況下它是安全的。還要注意,所有副本都很淺,因爲您正在存儲原始指針。

std::set<Value *> values = Ts.at(i).getValues(); // get copy of set 
for (std::set<Value *>::iterator it = values.begin(), ite = values.end(); it != ite; ++it) { // using iterators from the same set, this is OK 
+0

謝謝,這是有道理的。你還可以解釋爲什麼第一個「修復」工作?即單獨留下循環迭代器,但改變項目添加到'Ts'向量的方式?它只是運氣嗎? – 2014-09-24 16:22:11

+0

我也一直在分析,但沒有找到合理的理由來工作。唯一的區別似乎是具有1個元素而不是2個集合。爲什麼所有在第一個「修復」中工作的東西仍然看起來像一個未定義行爲的僞裝案例。 – 2014-09-24 16:26:40