C++ 迭代时从std::set中删除元素
我需要遍历一个集合并删除满足预定义条件的元素 这是我编写的测试代码:C++ 迭代时从std::set中删除元素,c++,iterator,set,std,c++-standard-library,C++,Iterator,Set,Std,C++ Standard Library,我需要遍历一个集合并删除满足预定义条件的元素 这是我编写的测试代码: #include <set> #include <algorithm> void printElement(int value) { std::cout << value << " "; } int main() { int initNum[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 }; std::set<int>
#include <set>
#include <algorithm>
void printElement(int value) {
std::cout << value << " ";
}
int main() {
int initNum[] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };
std::set<int> numbers(initNum, initNum + 10);
// print '0 1 2 3 4 5 6 7 8 9'
std::for_each(numbers.begin(), numbers.end(), printElement);
std::set<int>::iterator it = numbers.begin();
// iterate through the set and erase all even numbers
for (; it != numbers.end(); ++it) {
int n = *it;
if (n % 2 == 0) {
// wouldn't invalidate the iterator?
numbers.erase(it);
}
}
// print '1 3 5 7 9'
std::for_each(numbers.begin(), numbers.end(), printElement);
return 0;
}
编辑:首选解决方案
我想出了一个对我来说更优雅的解决方案,尽管它的效果完全相同
while(it != numbers.end()) {
// copy the current iterator then increment it
std::set<int>::iterator current = it++;
int n = *current;
if (n % 2 == 0) {
// don't invalidate iterator it, because it is already
// pointing to the next element
numbers.erase(current);
}
}
while(it!=numbers.end()){
//复制当前迭代器,然后将其递增
std::set::iterator current=it++;
int n=*当前值;
如果(n%2==0){
//不要使迭代器失效,因为它已经失效了
//指向下一个元素
数字。擦除(当前);
}
}
如果while中有多个测试条件,则每个测试条件都必须递增迭代器。我更喜欢这段代码,因为迭代器只在一个地方递增,这样代码就不那么容易出错,可读性也更高。这种行为是特定于实现的。为了保证迭代器的正确性,如果需要删除元素,则应使用“it=numbers.erase(it);”语句,并在其他情况下简单地激活迭代器。这取决于实现: 标准23.1.2.8: 插入成员不得影响迭代器和对容器的引用的有效性,擦除成员应仅使迭代器和对已擦除元素的引用无效 也许你可以试试这个--这是符合标准的:
for (auto it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
numbers.erase(it++);
}
else {
++it;
}
}
请注意,它是后缀,因此它会传递旧位置进行擦除,但由于运算符的原因,它会首先跳转到新位置
2015.10.27更新:
C++11解决了该缺陷<代码>迭代器擦除(常量迭代器位置)返回一个迭代器到最后一个删除的元素后面的元素(或者set::end
,如果最后一个元素被删除)。所以C++11的风格是:
for (auto it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
it = numbers.erase(it);
}
else {
++it;
}
}
你误解了“未定义的行为”的含义。未定义的行为并不意味着“如果您这样做,您的程序将崩溃或产生意外的结果”。它意味着“如果您这样做,您的程序可能崩溃或产生意外的结果”,或者根据您的编译器、您的操作系统、月相等执行任何其他操作
如果某个东西执行时没有崩溃,并且按照您期望的方式运行,这并不能证明它不是未定义的行为。它所证明的是,在特定的操作系统上使用特定的编译器编译之后,它的行为恰好与在特定的运行中观察到的一样
从集合中删除一个元素会使被删除元素的迭代器失效。使用无效迭代器是未定义的行为。碰巧在这个特殊的例子中,观察到的行为是你想要的;这并不意味着代码是正确的。如果通过valgrind运行程序,您将看到大量读取错误。换句话说,是的,迭代器是无效的,但是在您的示例中,您是幸运的(或者说非常不幸,因为您没有看到未定义行为的负面影响)。一种解决方案是创建一个临时迭代器,增加临时值,删除目标迭代器,然后将目标设置为临时值。例如,按如下方式重新编写循环:
std::set<int>::iterator it = numbers.begin();
std::set<int>::iterator tmp;
// iterate through the set and erase all even numbers
for ( ; it != numbers.end(); )
{
int n = *it;
if (n % 2 == 0)
{
tmp = it;
++tmp;
numbers.erase(it);
it = tmp;
}
else
{
++it;
}
}
std::set::iterator it=numbers.begin();
std::set::迭代器tmp;
//遍历集合并删除所有偶数
for(;it!=number.end();)
{
int n=*it;
如果(n%2==0)
{
tmp=it;
++tmp;
数字。删除(它);
它=tmp;
}
其他的
{
++它;
}
}
只是警告一下,对于deque容器,所有检查deque迭代器是否等于numbers.end()的解决方案都可能在gcc 4.8.4上失败。也就是说,删除deque的一个元素通常会使指向数字的指针无效。end()
请注意,虽然deque转换在这种特殊情况下是正确的,但结束指针在这一过程中已经失效。对于不同尺寸的三角形,误差更为明显:
int main()
{
deque<int> numbers;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
deque<int>::iterator it_end = numbers.end();
for (deque<int>::iterator it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
cout << "Erasing element: " << *it << "\n";
numbers.erase(it++);
if (it_end == numbers.end()) {
cout << "it_end is still pointing to numbers.end()\n";
} else {
cout << "it_end is not anymore pointing to numbers.end()\n";
}
}
else {
cout << "Skipping element: " << *it << "\n";
++it;
}
}
}
以下是解决此问题的方法之一:
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> numbers;
bool done_iterating = false;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
if (!numbers.empty()) {
deque<int>::iterator it = numbers.begin();
while (!done_iterating) {
if (it + 1 == numbers.end()) {
done_iterating = true;
}
if (*it % 2 == 0) {
cout << "Erasing element: " << *it << "\n";
numbers.erase(it++);
}
else {
cout << "Skipping element: " << *it << "\n";
++it;
}
}
}
}
#包括
#包括
使用名称空间std;
int main()
{
德克数;
bool done_迭代=false;
数字。推回(0);
数字。推回(1);
数字。推回(2);
数字。推回(3);
数字。推回(4);
如果(!numbers.empty()){
迭代器it=numbers.begin();
当(!完成迭代){
if(it+1==numbers.end()){
完成迭代=真;
}
如果(*it%2==0){
cout我遇到了同样的老问题,发现下面的代码更容易理解,这在某种程度上与上述解决方案一致
std::set<int*>::iterator beginIt = listOfInts.begin();
while(beginIt != listOfInts.end())
{
// Use your member
std::cout<<(*beginIt)<<std::endl;
// delete the object
delete (*beginIt);
// erase item from vector
listOfInts.erase(beginIt );
// re-calculate the begin
beginIt = listOfInts.begin();
}
std::set::iterator beginIt=listOfInts.begin();
while(beginIt!=listOfInts.end())
{
//使用您的会员
std::cout我认为在尝试删除迭代器包装的对象时,使用STL方法“remove\u if
”可以帮助防止出现一些奇怪的问题
这种解决方案可能效率较低
假设我们有一些
int main()
{
deque<int> numbers;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
deque<int>::iterator it_end = numbers.end();
for (deque<int>::iterator it = numbers.begin(); it != numbers.end(); ) {
if (*it % 2 == 0) {
cout << "Erasing element: " << *it << "\n";
numbers.erase(it++);
if (it_end == numbers.end()) {
cout << "it_end is still pointing to numbers.end()\n";
} else {
cout << "it_end is not anymore pointing to numbers.end()\n";
}
}
else {
cout << "Skipping element: " << *it << "\n";
++it;
}
}
}
Erasing element: 0
it_end is still pointing to numbers.end()
Skipping element: 1
Erasing element: 2
it_end is still pointing to numbers.end()
Skipping element: 3
Erasing element: 4
it_end is not anymore pointing to numbers.end()
Erasing element: 0
it_end is not anymore pointing to numbers.end()
Erasing element: 0
it_end is not anymore pointing to numbers.end()
...
Segmentation fault (core dumped)
#include <iostream>
#include <deque>
using namespace std;
int main()
{
deque<int> numbers;
bool done_iterating = false;
numbers.push_back(0);
numbers.push_back(1);
numbers.push_back(2);
numbers.push_back(3);
numbers.push_back(4);
if (!numbers.empty()) {
deque<int>::iterator it = numbers.begin();
while (!done_iterating) {
if (it + 1 == numbers.end()) {
done_iterating = true;
}
if (*it % 2 == 0) {
cout << "Erasing element: " << *it << "\n";
numbers.erase(it++);
}
else {
cout << "Skipping element: " << *it << "\n";
++it;
}
}
}
}
std::set<int*>::iterator beginIt = listOfInts.begin();
while(beginIt != listOfInts.end())
{
// Use your member
std::cout<<(*beginIt)<<std::endl;
// delete the object
delete (*beginIt);
// erase item from vector
listOfInts.erase(beginIt );
// re-calculate the begin
beginIt = listOfInts.begin();
}
Bullet::Ptr is a shared_pr<Bullet>
auto it = std::remove_if(m_bullets.begin(), m_bullets.end(), [](Bullet::Ptr bullet){
// dead bullets need to be removed from the container
if (!bullet->isAlive()) {
// lambda function returns true, thus this element is 'removed'
return true;
}
else{
// in the other case, that the bullet is still alive and we can do
// stuff with it, like rendering and what not.
bullet->render(); // while checking, we do render work at the same time
// then we could either do another check or directly say that we don't
// want the bullet to be removed.
return false;
}
});
// The interesting part is, that all of those objects were not really
// completely removed, as the space of the deleted objects does still
// exist and needs to be removed if you do not want to manually fill it later
// on with any other objects.
// erase dead bullets
m_bullets.erase(it, m_bullets.end());
std::erase_if(numbers, [](int n){ return n % 2 == 0 });