2014-09-24 132 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