i!=cend();// и
++i) {// стирает все элементы с
if (badValue(*i)) c.erase
}// возвращает true.
// Не
Выполнение этого фрагмента приводит к непредсказуемым результатам. При стирании элемента контейнера все итераторы, указывающие на этот элемент, становятся недействительными. Таким образом, после возврата из c.erase(i) итератор i становится недействительным. Для нашего цикла это фатально, поскольку после вызова erase итератор i увеличивается (++i в заголовке цикла fo
Проблема решается просто: необходимо позаботиться о том, чтобы итератор переводился на следующий элемент с перед вызовом erase. Это проще всего сделать постфиксным увеличением i при вызове:
i!=c.end(); // цикла for пуста; i теперь
/* пусто */) {// изменяется внутри цикла
if (badValue(*i)) c.erase(i++);// Для удаляемых элементов
else ++i;// передать erase текущее
}// значение i и увеличить i.
// Для остающихся элементов // просто увеличить i
Новый вариант вызова erase работает, поскольку выражение i++ равно старому значению i, но у него имеется побочный эффект — приращение i. Таким образом, мы передаем старое (не увеличенное) значение i и увеличиваем i перед вызовом erase. Именно это нам и требовалось. Хотя это решение выглядит просто, лишь немногие программисты предложат его с первой попытки.
Пора сделать следующий шаг. Помимо простого удаления всех элементов, для которых badValue возвращает true, мы также хотим регистрировать каждую операцию удаления в журнале.
Для ассоциативных контейнеров задача решается очень просто, поскольку она требует лишь тривиальной модификации созданного цикла:
ofstream logFile;// Файл журнала
i!=c.end();) {// без изменений
if (badValue(*i)) {
logFile«"Erasing "« *i «'\n'; // Вывод в журнал
c.erase(i++):// Удаление
}
else ++i:
}
На этот раз хлопоты возникают с vecto
Следовательно, с vecto
for
if (badValue(*i)) {
logFile«"Erasing "«*i«'\n';
i=c.erase());// Сохраняем действительный итератор i,
}// для чего ему присваивается значение,
else ++i;// возвращаемое функцией erase
}
Такое решение превосходно работает, но только для стандартных последовательных контейнеров. По весьма сомнительным причинам (совет 5) функция erase для стандартных ассоциативных контейнеров возвращает void. В этом случае приходится использовать методику с постфиксным приращением итератора, переданного erase. Кстати говоря, подобные различия между последовательными и ассоциативными контейнерами — один из примеров того, почему контейнерно-независимый код обычно считается нежелательным (совет 2).