【问题标题】:Deleting last element in a std::vector of std::shared_ptr while iterating over it causes segmentation fault [duplicate]在迭代它时删除 std::shared_ptr 的 std::vector 中的最后一个元素会导致分段错误 [重复]
【发布时间】:2019-09-21 20:15:24
【问题描述】:

请尝试编译并运行以下代码。 当迭代共享指针向量时,我必须删除最后一个元素,这将导致分段错误,但我不明白为什么for 迭代在el_it 达到v.end() 时不会中断,并且我必须手动完成(注释代码)。

#include <vector>
using std::vector;

#include <memory>
using std::shared_ptr;

#include <algorithm>
using std::remove;

class A {
public:
    A(int age) { age_ = age; }
    int age_ = 0;
    int alive_ = 1;
};

int main() {

    shared_ptr<A> a0(new A(0));
    shared_ptr<A> a1(new A(1));
    shared_ptr<A> a2(new A(2));
    vector< shared_ptr <A> > v;
    v.push_back(a0);
    v.insert(v.end(), a1);
    v.insert(v.end(), a2);
    for (auto el_it = v.begin(); el_it != v.end(); ++ el_it) {
        auto el = *el_it;
        if (el->age_ == 2) {
            v.erase(el_it);
        }
        /*
        if (el_it == v.end()) // Why is this required ??
            break;
        */

    }
    return 0;

}

【问题讨论】:

  • Iterator invalidation rules相关且可能重复
  • "if (el_it == v.end()) // Why is this required ??",其实没有,还有UB。
  • @P.W 耶! #keepduping
  • @LightnessRacesinOrbit: :-)。最近没有任何添加到 C++faq 标签中。有任何贡献吗?

标签: c++ vector shared-ptr


【解决方案1】:

变化:

v.erase(el_it);

el_it = v.erase(el_it); // don't do this yet, read below

擦除会使迭代器无效。擦除的返回值是下一个元素的有效迭代器。因此,对于我上面写的内容,您正在跳过一个元素。我会推荐这个:

for (auto el_it = v.begin(); el_it != v.end();) {
    auto el = *el_it;
    if (el->age_ == 2) {
        el_it = v.erase(el_it);
    } else {
        ++ el_it;
    }
}

【讨论】:

  • ++ el_it 在这种情况下也应该被删除
  • 现在没有意义,问题已经被欺骗了
  • 您忘记将el_it = v.erase(el_it); 合并到代码中。
  • @nwp 完成。谢谢。
  • @DomenicoDiIorio 第一个在您的情况下无效。您必须在擦除元素后更新迭代器。这是我的错字。
【解决方案2】:

调用 std::vector::erase() 会使您的 el_it 迭代器无效。你没有考虑到这一点。 erase() 将新的迭代器返回到您已删除的元素之后的元素。您需要使用新的迭代器来继续循环。

试试这个:

for (auto el_it = v.begin(); el_it != v.end();) {
    auto el = *el_it;
    if (el->age_ == 2) {
        el_it = v.erase(el_it);
    } else {
        ++el_it;
    }
}

另外,更好的选择是通过std::remove_if() 算法使用erase-remove idiom,而不是手动循环:

v.erase(
    std::remove_if(v.begin, v.end(),
        [](auto el){ return (el->age_ == 2); } 
    ),
    v.end()
);

C++20 增加了std::erase_if() 使其更容易:

std::erase_if(v,
    [](auto el){ return (el->age_ == 2); } 
);

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-08-29
    • 1970-01-01
    • 2019-08-02
    • 1970-01-01
    • 2014-11-08
    • 2011-07-13
    • 1970-01-01
    • 2019-01-15
    相关资源
    最近更新 更多