在遍历它时删除 std::vector of std::shared_ptr 中的最后一个元素会导致分段错误
Deleting last element in a std::vector of std::shared_ptr while iterating over it causes segmentation fault
请尝试编译并运行以下代码。
当迭代共享指针向量时,我必须删除最后一个元素,这将导致分段错误,但我不明白为什么 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;
}
变化:
v.erase(el_it);
到
el_it = v.erase(el_it); // don't do this yet, read below
擦除会使迭代器失效。 erase 的 return 值是下一个元素的有效迭代器。因此,对于我上面所写的内容,您将跳过一个元素。我推荐这个:
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::vector::erase()
会使您的 el_it
迭代器无效。你没有考虑到这一点。 erase()
returns 一个新的迭代器指向已删除元素之后的元素。您需要使用那个新的迭代器来继续循环。
试试这个:
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;
}
}
或者,更好的选择是使用 erase-remove idiom via the std::remove_if()
算法,而不是完全手动循环:
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); }
);
请尝试编译并运行以下代码。
当迭代共享指针向量时,我必须删除最后一个元素,这将导致分段错误,但我不明白为什么 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;
}
变化:
v.erase(el_it);
到
el_it = v.erase(el_it); // don't do this yet, read below
擦除会使迭代器失效。 erase 的 return 值是下一个元素的有效迭代器。因此,对于我上面所写的内容,您将跳过一个元素。我推荐这个:
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::vector::erase()
会使您的 el_it
迭代器无效。你没有考虑到这一点。 erase()
returns 一个新的迭代器指向已删除元素之后的元素。您需要使用那个新的迭代器来继续循环。
试试这个:
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;
}
}
或者,更好的选择是使用 erase-remove idiom via the std::remove_if()
算法,而不是完全手动循环:
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); }
);