2010-09-05 3 views
4

Я в основном цикл через все записи, чтобы проверить, является ли некоторые записи будут стерты, но кажется неправильным образом:Как удалить записи из вектора в C++?

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

Любое пятно проблема в этом? Как это сделать правильно?

ответ

4

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

Безобразная хак будет сделать следующее:

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

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

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

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

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

+0

также может заменить 'if (...)' на 'while (...)', пока условие может также указывать, когда оно заканчивается концом вектора. – goldPseudo

+0

Я не совсем понимаю решение GMan, поэтому я принимаю это h ack версия. – user198729

+1

@user: вы должны попросить разъяснения и/или получить [книгу] (http://stackoverflow.com/questions/388242/the-definitive-c++-book-guide-and-list), чтобы научить вас C++. – GManNickG

8

Вы можете использовать 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 Lambda в то же самое, конечно.

0

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

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

    ++it; 
} 
+0

Это, кажется, самое простое решение, но я не уверен, действительно ли оно работает? – user198729

+3

Я предполагаю, что после стираемой записи будет пропущен 'it ++' – user198729

+0

, это немного сломано, вам не нужно делать thr 'it ++', если if is true (иначе вы можете пропустить элемент или, что еще хуже, вы можете пропустите над 'end()'. Лучше было бы это: 'for (it = v.begin(); it! = v.end();) {if (...) it = v.erase (it); else ++ it;} ' –

1

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

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

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

Смежные вопросы