我的问题相当简短:
vector<int> ints{1,2,3,4};
int &y = ints[0];
// ints.push_back(y);
y = 5;
for (auto &x : ints) {
cout << x << ", ";
}
为什么在评论时您获得了5, 2, 3, 4
,但当您取消注释ints.push_back(y);
时,您获得了1, 2, 3, 4, 1
。为了更清楚地了解问题,我会再写一次:你得到[-->1<--], 2, 3, 4, 1
,而不是5, 2, 3, 4, 1
甚至是5, 2, 3, 4, 5
这种行为让我疯狂......引擎盖下会发生什么?
答案 0 :(得分:15)
当你向后推到一个向量时,如果向量没有足够的内存来分配存储新元素,它会分配一个新的更大的内存块,移动元素并释放旧内存。如果发生这种情况,任何对向量元素的引用或指针都将失效。
因此,在推回之后,当您将5
分配给y
时,它是未定义的行为,因为y
是无效的引用,并且没有理由期望赋值对向量的任何元素都有影响。
答案 1 :(得分:4)
您需要考虑调用std::vector::push_back
时可能发生的重新分配。
int &y = ints[0];
ints.push_back(y);
y = 5;
如果执行此类重新分配,则第三条指令可能会导致未定义的行为。如果发生这种情况,您将失去所有可以想到的保证。
取出这个条件,通过reserv
内存,显示预期结果
5,2,3,4,1,
std::vector<int> ints{1,2,3,4};
ints.reserve(128); // Note
int &y = ints[0];
ints.push_back(y);
y = 5;
cppreference处的表格显示了精确发生的事情以及迭代器失效的时间。
通常,您不希望对容器中存储的可能无效的元素进行免费引用或指针;如果真的需要,请重新加载地址或选择一个容器,提供更多关于重新分配的保证,例如std::deque
,std::forward_list
或std::list
。
答案 2 :(得分:1)
我相信你正在推回一份副本。原因是你的vector包含int类型而不是type&amp; int所以它通过制作一个解引用副本来隐式地转换引用。
在注释了push_back的示例中,您没有将副本添加到向量的末尾,并且仍然有一个指向第一个元素的引用,这意味着您可以通过引用更改元素。但是,一旦执行push_back,矢量就会调整大小并移动其中的对象,使任何引用或指针无效。
您可以通过简单地将矢量设置为更大的尺寸来清楚地看到这一点。即。
vector<int> ints(5);
ints = {1,2,3,4};
int &y = ints[0];
ints.push_back(y);
y = 5;
for (auto &x : ints) {
cout << x << ", ";
}
// output = 5,2,3,4,1