Как стереть записи из вектора в C ++? - PullRequest
4 голосов
/ 05 сентября 2010

Я в основном перебираю все записи, чтобы проверить, удаляются ли некоторые записи, но, похоже, неправильно:

std::vector<HANDLE> myvector; 
for(unsigned int i = 0; i < myvector.size(); i++)
{
    if(...)
         myvector.erase(myvector.begin()+i);
}

Кто-нибудь определит проблему в этом? Как это сделать правильно?

Ответы [ 4 ]

8 голосов
/ 05 сентября 2010

Вы можете использовать std::remove_if. Это переместит все оставшиеся элементы на передний план и вернет итератор на новый задний план. Затем вы можете стереть его:

struct my_predicate
{
    bool operator()(HANDLE) const
    {
        return ...;
    }
};

typedef std::vector<HANDLE> vector_type;

vector_type::iterator newEnd =
    std::remove_if(myvector.begin(), myvector.end(), my_predicate());

myvector.erase(newEnd, myvector.end());

Обычно это делается в одну строку. Если ваш компилятор поддерживает лямбда (C ++ 0x), вы можете сделать:

vector_type::iterator newEnd =
    std::remove_if(myvector.begin(), myvector.end(), [](HANDLE){ return ... });

myvector.erase(newEnd, myvector.end());

Чтобы сохранить предикат локальным.


Если вы думаете, что это безобразно, просто оберните это:

template <typename Vec, typename Pred>
Pred erase_if(Vec& pVec, Pred pPred)
{
    pVec.erase(std::remove_if(pVec.begin(), pVec.end(),
                                pPred), pVec.end());

    return pPred;
}

Тогда:

erase_if(myvector, mypredicate);

C ++ 0x лямбда-работа, конечно, такая же.

4 голосов
/ 05 сентября 2010

Ваша проблема алгоритмическая.Что произойдет, если два соседних элемента соответствуют вашему критерию удаления?Первый будет удален, но поскольку i увеличивается после каждой итерации цикла, второй будет пропущен.Это связано с тем, что вектор является непрерывным в памяти, и все элементы после удаленного перемещаются на один индекс вперед.

Гадкий хак может сделать следующее:

std::vector<HANDLE> myvector; 
for(unsigned int i = 0; i < myvector.size();)
{
    if(...)
         myvector.erase(myvector.begin()+i);
    else
         i++;
}

I 'Я не уверен, что использование итераторов будет работать, потому что вызов erase делает недействительными итераторы для элементов после стертого элемента.

Элегантным решением будет использование std :: remove_if , как предложил GMan,Это отвлечет две вещи:

  1. Ваше условие удаления
  2. Процесс, с помощью которого удаляются элементы контейнера

Редактировать: Я должен такжедобавим, взломанное решение - O (n 2 ) в худшем случае.Решение GMan - O (n) , при условии, что ваше условие удаления равно O (1) .Я настоятельно рекомендую вам изучить и использовать решение GMan.

1 голос
/ 06 сентября 2010

Возможно, еще одно хакерское решение ...

std::vector<HANDLE> myvector; 
for(unsigned int i = myvector.size()-1; i >=0; --i)
{
    if(...)
         myvector.erase(myvector.begin()+i);
}

Но, по крайней мере, все просто.

0 голосов
/ 05 сентября 2010

Для этого следует использовать векторный итератор, И , при удалении назначить итератору значение, возвращаемое erase()

for (it = myvector.begin(); it != myvector.end();) {
    if (...) {
        it = myvector.erase(it);
        continue;
    }

    ++it;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...