C++:在迭代std :: vector时使用push_back

8

以下代码片段输出非常奇怪。我本来期望一个溢出错误(Python会给出MemoryError)。

#include <iostream>
#include <vector>

int main()
{
    std::vector<int> a{1,2,3};

    for( auto const & item : a)
        a.push_back(item);


    for( auto const & item : a)
        std::cout<<item<<',';

    return 0;
}

输出结果:1,2,3,1,0,3,

这个结果怎么解释呢?

如果你在Python中做了类似的事情,它会出现内存错误。

>>> a = range(0,20)
>>> for i in a:
    a.append(i)



Traceback (most recent call last):
  File "<pyshell#3>", line 2, in <module>
    a.append(i)
MemoryError

>>> 

这个问题浮现在我的脑海中,因为上面的代码写法被认为是绑定安全的。对于绑定安全的容器,在 foreach type iteration期间,容器不应该增长/缩小。所以这是一个漏洞抽象。
有没有一种方法可以包装这个foreach循环,以便在循环体中不允许引起大小修改/重新分配的任何操作。

我怀疑这样做是未定义行为。 - πάντα ῥεῖ
@Gautam Jha,我认为在问题得到回答之后修改/扩展问题有点不公平... - Juan Leni
之前意图表达不够清晰,提出一个类似的问题并不能解决问题。 - g-217
好的,我认为在最后两段你现在提出了一个不同的问题。 - Juan Leni
2个回答

18
在C ++中,向向量添加元素可能会导致包含数据的重新分配,这将使所有迭代器无效。这意味着您不能使用迭代器(这是范围为基础的循环所做的)循环遍历向量同时插入新元素。
但是,您可以使用索引进行迭代,并使用向量大小作为条件,因为索引始终相同。

这个问题浮现于我的脑海中,因为上述编写代码的方式被认为是绑定安全的。但它难道不是一个有缺陷的抽象吗? - g-217
C++委员会的每个人都支持这些循环,并为边界安全进行担保。如果容器的大小不是常数,我们就无法实现边界安全。 - g-217
这个答案太棒了。因为这个问题我都快哭出来了。 - Nurbol Alpysbayev

5
如果向量被重新调整大小,迭代器将变得无效。
如果您提前进行保留,则可以执行此操作。
请记住,对于for range,它将在任何更改之前定义的迭代器边界上运行。因此只会获取附加到列表中的副本。
#include <iostream>
#include <vector>

int main()
{
    std::vector<int> a{1,2,3};

    a.reserve(10);           // 10 should be enough to get a copy without reallocating
    for( auto const & item : a)
        a.push_back(item);

    for( auto const & item : a)
        std::cout<<item<<',';

    return 0;
}

输出:

1,2,3,1,2,3,

我不建议使用这样的方法,因为我认为它不够清晰明了。然而,如果你参照标准,这个行为是可以预期的:

23.3.6.5 向量修饰符

关于 insert,emplace,emplace_back, push_back 的使用。

备注:如果新大小大于旧容量,则会导致重新分配。如果没有发生重新分配,则插入点之前的所有迭代器和引用仍然有效。

也就是说,如果没有重新分配,你可以信任插入点之前的迭代器。只要你的向量容量足够大,你就可以无问题地添加元素。


为什么C++不会使std::vector的大小恒定,即不进行重新分配。因为上述代码没有任何意义。 - g-217
std::vector 能够动态增长。如果您想要大小保持不变,应该使用 std::array - Juan Leni
这个问题浮现在我的脑海中,因为上述编写代码的方式被认为是绑定安全的。但它是否存在泄漏的抽象呢? - g-217
@mtk99,std::vectorstd::array之间还有一个关键的区别,这可能会使std::array无法使用:向量在堆上分配内存,而数组是由编译器在编译时在堆栈上分配的(如果定义为局部变量)。 - Some programmer dude
@GautamJha。我附加了一些关于C++标准的信息。 - Juan Leni

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接