Удалить элемент из вектора во время итерации?

У меня есть вектор, который содержит элементы, которые являются активными или неактивными. Я хочу, чтобы размер этого вектора оставался небольшим из-за проблем с производительностью, поэтому я хочу, чтобы элементы, помеченные как неактивные, были удалены из вектора. Я пытался сделать это во время итерации, но я получаю сообщение об ошибке "несовместимые векторные итераторы".

vector<Orb>::iterator i = orbsList.begin();

    while(i != orbsList.end()) {
        bool isActive = (*i).active;

        if(!isActive) {
            orbsList.erase(i++);
        }
        else {
            // do something with *i
            ++i;
        }
    }

7 ответов

Самый читаемый способ, которым я занимался в прошлом, это использовать std::vector::erase в сочетании с std::remove_if, В приведенном ниже примере я использую эту комбинацию для удаления любого числа, меньшего 10, из вектора.

(Для не-C++0x вы можете просто заменить лямбду, указанную ниже, своим собственным предикатом:)

// a list of ints
int myInts[] = {1, 7, 8, 4, 5, 10, 15, 22, 50. 29};
std::vector v(myInts, myInts + sizeof(myInts) / sizeof(int));

// get rid of anything < 10
v.erase(std::remove_if(v.begin(), v.end(), 
                       [](int i) { return i < 10; }), v.end());

Я согласен с ответом Уилкса. Вот реализация:

// curFiles is: vector < string > curFiles;

vector< string >::iterator it = curFiles.begin();

while(it != curFiles.end()) {

    if(aConditionIsMet) {

        it = curFiles.erase(it);
    }
    else ++it;
}

Вы можете сделать это, но вам придется перетасовать while() немного, я думаю. erase() Функция возвращает итератор для элемента, следующего после стертого: iterator erase(iterator position);, Цитирование из стандарта от 23.1.1/7:

Итератор, возвращаемый из a.erase(q), указывает на элемент, следующий сразу за q до того, как элемент будет удален. Если такого элемента не существует, возвращается a.end().

Хотя, возможно, вам стоит использовать идиому Erase-remove.

erase возвращает указатель на следующее значение итератора (аналогично Vassilis):

vector <cMyClass>::iterator mit
for(mit = myVec.begin(); mit != myVec.end(); )
{   if(condition)
        mit = myVec.erase(mit);
    else
        mit++;
}

Если кому-то нужно работать над индексами

vector<int> vector;
for(int i=0;i<10;++i)vector.push_back(i);

int size = vector.size();
for (int i = 0; i < size; ++i)
{
    assert(i > -1 && i < (int)vector.size());
    if(vector[i] % 3 == 0)
    {
        printf("Removing %d, %d\n",vector[i],i);
        vector.erase(vector.begin() + i);
    }

    if (size != (int)vector.size())
    {
        --i;
        size = vector.size();
        printf("Go back %d\n",size);
    }
}

Вы можете рассмотреть возможность использования std::list вместо std::vector для вашей структуры данных. Безопаснее (меньше подвержено ошибкам) ​​использовать при сочетании стирания с итерацией.

Как они сказали, итераторы вектора становятся недействительными на vector::erase() независимо от того, какую форму приращения итератора вы используете. Вместо этого используйте целочисленный индекс.

Удаление элементов из середины вектора сделает недействительными все итераторы этого вектора, поэтому вы не можете этого сделать (обновите: не прибегая к предложению Уилкса).

Кроме того, если вы беспокоитесь о производительности, удаление элементов из середины вектора в любом случае является плохой идеей. Возможно, вы хотите использовать std::list?

Другие вопросы по тегам