【问题标题】:std::vector iterator invalidationstd::vector 迭代器失效
【发布时间】:2011-04-14 10:40:32
【问题描述】:

之前有几个关于这个问题的问题;我的理解是,调用 std::vector::erase 只会使位于已擦除元素 之后 位置的迭代器无效。但是,在擦除元素后,该位置的迭代器是否仍然有效(当然,前提是它在擦除后不指向end())?

我对如何实现向量的理解似乎表明迭代器绝对可用,但我不完全确定它是否会导致未定义的行为。

作为我正在谈论的示例,以下代码从向量中删除所有奇数。此代码是否会导致未定义的行为?

typedef std::vector<int> vectype;
vectype vec;

for (int i = 0; i < 100; ++i) vec.push_back(i);

vectype::iterator it = vec.begin();
while (it != vec.end()) {
    if (*it % 2 == 1) vec.erase(it);
    else ++it;
}

代码在我的机器上运行良好,但这并不能让我相信它是有效的。

【问题讨论】:

    标签: c++ stl vector iterator


    【解决方案1】:

    删除一个元素后,该位置的迭代器是否仍然有效

    没有;传递给erase 的迭代器处或之后的所有迭代器均无效。

    但是,erase 返回一个新的迭代器,它指向紧跟在被擦除的元素之后的元素(如果没有这样的元素,则指向末尾)。您可以使用此迭代器来恢复迭代。


    请注意,这种删除奇数元素的特殊方法效率很低:每次删除一个元素时,它之后的所有元素都必须在向量中向左移动一个位置(这是 O(n 2))。您可以使用erase-remove idiom (O(n)) 更有效地完成此任务。您可以创建一个is_odd 谓词:

    bool is_odd(int x) { return (x % 2) == 1; }
    

    那么这可以传递给remove_if:

    vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd), vec.end());
    

    【讨论】:

    • 为什么x 是通过常量引用而不是值传递?
    • @James 但是上面提供的代码是如何工作的,因为擦除会使迭代器失效?
    【解决方案2】:

    或者:

    class CIsOdd
    {
    public:
        bool operator()(const int& x) { return (x % 2) == 1; }
    };
    
    vec.erase(std::remove_if(vec.begin(), vec.end(), CIsOdd()), vec.end());
    

    【讨论】:

    • 为什么x 是通过常量引用而不是值传递?
    • 问题:为什么人们经常喜欢这里的方法(只有 operator() 重载的函子,即没有状态/数据)而不是像 James McNellis 上面的回答那样的简单独立函数?我知道它们都会起作用,但我通常采用与 James 相同的方法,对我来说这似乎更简单、更明显。在某个时刻,我开始问自己,“我错过了什么?”如果只是个人喜好,没关系。
    • @FredOverflow,x 可以是结构,@Dan,...并且 operator() 是该结构的成员
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-05-06
    • 1970-01-01
    • 2012-05-07
    • 2014-11-08
    • 2013-06-18
    • 2018-04-27
    • 1970-01-01
    相关资源
    最近更新 更多