pop_back()是否真的使std :: vector上的* all *迭代器无效?

时间:2008-09-15 12:28:49

标签: c++ stl

std::vector<int> ints;

// ... fill ints with random values

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
    {
        *it = ints.back();
        ints.pop_back();
        continue;
    }
    it++;
}

此代码无效,因为调用pop_back()时,it无效。但是我找不到任何关于std::vector::pop_back()中迭代器失效的文档。

你有关于那个的链接吗?

11 个答案:

答案 0 :(得分:12)

以下是您的答案,直接来自The Holy Standard:

23.2.4.2载体满足容器和可逆容器的所有要求(在23.1中的两个表中给出)和序列,包括大多数可选的序列要求(23.1.1)。
23.1.1.12表68 expressiona.pop_back() 返回typevoid 操作语义 a.erase( - a.end()) containervector,list,deque

请注意,a.pop_back等同于a.erase( - a.end())。看看矢量的擦除细节:

23.2.4.3.3 - 迭代器擦除(迭代器位置) - 效果 - 在擦除点之后使所有迭代器和引用无效

因此,一旦调用pop_back,任何先前最终元素(现在不再存在)的迭代器都将失效。

查看代码,问题是当你删除最后一个元素并且列表变空时,你仍然会增加它并离开列表的末尾。

答案 1 :(得分:10)

pop_back()的调用删除了向量中的最后一个元素,因此该元素的迭代器无效。 pop_back()调用使最后一个元素之前的项的迭代器无效,只有重新分配才会这样做。来自Josuttis的“C ++标准库参考”:

  

插入或移除元素   使引用,指针和   引用以下内容的迭代器   元件。如果插入导致   重新分配,它使所有人失效   引用,迭代器和指针。

答案 2 :(得分:4)

(我使用C ++ 0x工作草案obtainable here

中使用的编号方案

第732页的表94说pop_back(如果它存在于序列容器中)具有以下效果:

{ iterator tmp = a.end(); 
--tmp; 
a.erase(tmp); } 

23.1.1,第12点指出:

  

除非另有说明(明确地或通过根据其他功能定义函数),调用容器   成员函数或将容器作为参数传递给库函数不应使迭代器无效或更改   该容器中对象的值。

访问end()作为应用前缀 - 都没有这样的效果,擦除()但是:

23.2.6.4(关于vector.erase()第4点):

  

效果:在擦除点处或之后使迭代器和引用无效。

总而言之:pop_back()只会使符合标准的最后一个元素的迭代器失效。

答案 3 :(得分:3)

以下是SGI的STL文档(http://www.sgi.com/tech/stl/Vector.html)的引用:

[5]当重新分配内存时,向量的迭代器无效。此外,在向量中间插入或删除元素会使指向插入或删除点后面的元素的所有迭代器无效。因此,如果使用reserve()预分配尽可能多的内存,并且所有插入和删除都在向量的末尾,则可以防止向量的迭代器失效。

我认为,pop_back只会使指向最后一个元素和end()迭代器的迭代器失效。我们确实需要查看代码失败的数据,以及它无法确定发生了什么的方式。据我所知,代码应该可行 - 这种代码中的常见问题是迭代器上的元素和++的删除发生在同一个迭代中,就像@mikhaild指出的那样。但是,在这段代码中并非如此:调用pop_back时不会发生++。

当指向最后一个元素时,可能仍会发生错误,最后一个元素小于10.我们现在正在比较无效和end()。它可能仍然有效,但不能保证。

答案 4 :(得分:1)

迭代器仅在重新分配存储时失效。 Google是您的朋友:see footnote 5

您的代码因其他原因无效。

答案 5 :(得分:1)

pop_back()仅使指向最后一个元素的迭代器无效。从C ++标准库参考:

  

插入或移除元素   使引用,指针和   引用以下内容的迭代器   元件。如果插入导致   重新分配,它使所有人失效   引用,迭代器和指针。

所以要回答你的问题,不,它不会使所有迭代器失效。

但是,在您的代码示例中,当它指向最后一个元素并且值低于10时,它可以使 it 无效。在这种情况下,Visual Studio调试STL将标记迭代器如果无效,并进一步检查它是否等于end()将显示一个断言。

如果迭代器被实现为纯指针(就像它们可能在所有非调试STL向量的情况下那样),那么你的代码应该可行。如果迭代器不仅仅是指针,那么你的代码就不会处理这种正确删除最后一个元素的情况。

答案 6 :(得分:0)

错误是当“it”指向vector的最后一个元素时,如果此元素小于10,则删除最后一个元素。现在“它”指向ints.end(),接着“it ++”将指针移动到ints.end()+ 1,所以现在“它”从ints.end()运行,并且你得到了无限循环扫描所有你的记忆:)。

答案 7 :(得分:0)

“官方规范”是C ++标准。如果您无权访问C ++ 03的副本,可以从委员会的网站上获取最新的C ++ 0x草案:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2723.pdf

容器需求的“操作语义”部分指定pop_back()等效于{iterator i = end(); - 一世;擦除(ⅰ); }。擦除的[vector.modifiers]部分说“效果:在擦除点或之后使迭代器和引用无效。”

如果你想要直觉参数,pop_back是不失败的(因为标准容器中的value_types的破坏不允许抛出异常),所以它不能进行任何复制或分配(因为它们可以抛出),这意味着你可以猜测擦除元素和结束迭代器的迭代器是无效的,但其余的不是。

答案 8 :(得分:0)

如果指向了向量中的最后一项,

pop_back()只会使 无效。因此,只要向量中的最后一个int小于10,您的代码就会失败,如下所示:

* it = ints.back(); //将*设置为已有的值    ints.pop_back(); //使迭代器无效
   继续; //循环并访问无效迭代器

答案 9 :(得分:0)

您可能需要考虑使用erase的返回值,而不是将后退元素交换到已删除的位置。对于序列,erase返回一个迭代器,指向元素一个超出要删除的元素。请注意,此方法可能会导致比原始算法更多的复制。

for(std::vector<int>::iterator it = ints.begin(); it != ints.end(); )
{
    if(*it < 10)
        it = ints.erase( it );
    else
        ++it;
}

std::remove_if也可以替代解决方案。

struct LessThanTen { bool operator()( int n ) { return n < 10; } };

ints.erase( std::remove_if( ints.begin(), ints.end(), LessThanTen() ), ints.end() );

std::remove_if(就像我的第一个算法一样)稳定,因此它可能不是最有效的方法,但它很简洁。

答案 10 :(得分:-1)

查看信息here (cplusplus.com)

  

删除最后一个元素

     

移除向量中的最后一个元素,有效地将向量大小减小一个,并使所有迭代器和对它的引用无效。