2010-09-19 2 views
12

Перед этим возникло несколько вопросов по этому вопросу; я понимаю, что вызов std::vector::erase приведет только к недействительным итераторам, которые находятся в позиции после стертый элемент. Однако, после стирания элемента, итератор в этой позиции все еще действителен (при условии, конечно, что он не указывает на end() после удаления)?std :: vector iterator invalidation

Мое понимание того, как вектор будет реализован, кажется, предполагает, что итератор определенно полезен, но я не совсем уверен, может ли это привести к неопределенному поведению.

В качестве примера того, что я говорю, следующий код удаляет все нечетные целые числа из вектора. Вызывает ли этот код неопределенное поведение?

typedef std::vector<int> vectype; 
vectype vec; 

for (int i = 0; i < 100; ++i) vec.push_back(i); 

vectype::iterator it = vec.begin(); 
while (it != vec.end()) { 
    if (*it % 2 == 1) vec.erase(it); 
    else ++it; 
} 

Код работает нормально на моей машине, но это не убеждает меня в том, что оно действительно.

ответ

31

после удаления элемента, является итератор в этой позиции до сих пор действительного

Нет; все итераторы в или после того, как итератор (ы), переданный в erase, признаны недействительными.

Однако erase возвращает новый итератор, который указывает на элемент сразу после стираемых элементов (или до конца, если такого элемента нет). Вы можете использовать этот итератор для возобновления итерации.


Обратите внимание, что этот конкретный метод удаления нечетных элементов довольно неэффективно: каждый раз, когда вы удалите элемент, все элементы после того, как он должен быть перемещен на одну позицию влево в векторе (это O (n)). Вы можете выполнить эту задачу гораздо более эффективно, используя erase-remove idiom (O (n)). Вы можете создать is_odd предикат:

bool is_odd(int x) { return (x % 2) == 1; } 

Тогда это может быть передана remove_if:

vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd), vec.end()); 
+1

Почему вы передаете 'x' по константной ссылке, а не по стоимость? – fredoverflow

+0

@Fred: Никакой особой причины; Спасибо что подметил это. –

+0

@James Но как работает вышеописанный код, так как стирание аннулирует итераторы? – Kapil

0

Или:

class CIsOdd 
{ 
public: 
    bool operator()(const int& x) { return (x % 2) == 1; } 
}; 

vec.erase(std::remove_if(vec.begin(), vec.end(), CIsOdd()), vec.end()); 
+1

Почему вы передаете 'x' ссылкой на const вместо значения? – fredoverflow

+2

Вопрос: почему люди часто предпочитают подход здесь (функтор с перегруженным только оператором(), т. Е. Состояние/данные) против простой автономной функции, такой как ответ Джеймса Макнеллиса выше? Я понимаю, что они оба будут работать, но я обычно придерживаюсь такого же подхода, как Джеймс, для меня это кажется более простым и очевидным. В какой-то момент я начинаю спрашивать себя: «Что мне не хватает?» Если это личное предпочтение, все в порядке. – Dan

+0

@FredOverflow, x может быть структурой, @Dan, ... и operator() членом этой структуры – ssianky