C ++中的奇怪引用行为

时间:2015-08-01 10:05:26

标签: c++

我的问题相当简短:

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

这种行为让我疯狂......引擎盖下会发生什么?

3 个答案:

答案 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::dequestd::forward_liststd::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