我想我对如何移动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");
}
然后程序不会崩溃。
我想了解导致这些崩溃的原因,以及程序之间的差异。
答案 0 :(得分:2)
我在这里找到的主要问题总结在你的代码的两行中:
std::set<Value *> getValues() { return values; }
此成员函数返回指针集的副本。
for (std::set<Value *>::iterator it = Ts.at(i).getValues().begin(), ite = Ts.at(i).getValues().end(); it != ite; ++it)
使用相同的逻辑行,该指令在初始化阶段创建两组。 ite
不是预期集合的结束迭代器,而是另一个新创建容器的结束指针。结果是,在it != ite
指向内存中的其他意外位置之前,很可能无法达到it
。
您的更正有效,因为现在您始终处理来自同一组的迭代器。副本仍然在这里发生,但在这种情况下它是安全的。另请注意,所有副本都很浅,因为您正在存储原始指针。
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