Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

АссоцКонтейнер<int> с;

for(АссоцКонтейнер<int>::iterator i=cbegin; // Наглядный, бесхитростный

i!=cend; // и ошибочный код, который

++i) { // стирает все элементы с

 if (badValue(*i)) c.erase(i); // для которых badValue

} // возвращает true.

// Не поступайте так!

Выполнение этого

фрагмента приводит к непредсказуемым результатам. При стирании элемента контейнера все итераторы, указывающие на этот элемент, становятся недействительными. Таким образом, после возврата из
c.erase(i)
итератор
i
становится недействительным. Для нашего цикла это фатально, поскольку после вызова
erase
итератор
i
увеличивается (
++i
в заголовке цикла
for
).

Проблема решается просто: необходимо позаботиться о том, чтобы итератор переводился на следующий элемент с перед вызовом

erase
. Это проще всего сделать постфиксным увеличением i при вызове:

АссоцКонтейнер<int> с;

for(АссоцКонтейнер<int>::iterator i=c.begin;// Третья часть заголовка

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;// Файл журнала

АссоцКонтейнер<int> с;

for(АссоцКонтейнер<int>::iterator i=c.begin;// Заголовок цикла остается

i!=c.end;) { // без изменений

 if(badValue(*i)) {

logFile<<"Erasing "<<*i<<'\n'; // Вывод в журнал

c.erase(i++);// Удаление

} else ++i;

}

На этот раз хлопоты возникают с

vector
,
string
и
deque
. Использовать идиому
erase/remove
не удается, поскольку
erase
или
remove_if
нельзя заставить вывести данные в журнал. Более того, вариант с циклом
for
, только что продемонстрированный для ассоциативных контейнеров, тоже не подходит, поскольку для контейнеров
vector
,
string
и
deque
он приведет к непредсказуемым последствиям. Вспомните, что для этих контейнеров в результате вызова
erase
становятся недействительными все итераторы, указывающие на удаляемый элемент. Кроме того, недействительными становятся все итераторы после удаляемого элемента, в нашем примере — все итераторы после
i
. Конструкции вида
i++
,
++i
и т. д. невозможны, поскольку ни один из полученных итераторов не будет действительным.

Следовательно, с

vector
,
string
и
deque
нужно действовать иначе. Мы должны воспользоваться возвращаемым значением erase, которое содержит именно то, что нам требуется — действительный итератор, который указывает на элемент, следующий за удаленным. Иначе говоря, программа выглядит примерно так:

for(ПослКонтейнер<int>::iterator = cbegin; i != cend; ) {

 if (badValue(*i)) {

logFile<<"Erasing "<<*i<<'\n';

i=c.erase); // Сохраняем действительный итератор i,

 } // для чего ему присваивается значение,

 else ++i; // возвращаемое функцией erase

}

Такое решение превосходно работает, но только для стандартных последовательных контейнеров. По весьма сомнительным причинам (совет 5) функция

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

Какое из этих решений лучше подойдет для контейнера

list
? Оказывается, в отношении перебора и удаления
list
может интерпретироваться как
vector/string/deque
или как ассоциативный контейнер — годятся оба способа. Обычно выбирается первый вариант, поскольку
list
, как и
vector/string/deque
, принадлежит к числу последовательных контейнеров. С точки зрения опытного программиста STL программа, в которой перебор и удаление из
list
производятся по правилам ассоциативных контейнеров, выглядит странно.

Подводя итог всему, о чем рассказывалось в этом совете, мы приходим к следующим заключениям.

Удаление всех объектов с заданным значением:

• контейнеры

vector
,
string
и
deque
: используйте идиому
erase/remove
;

• контейнер

list
: используйте
list::remove
;

• стандартный ассоциативный контейнер: используйте функцию

erase
.

Удаление всех объектов, соответствующих заданному предикату:

Поделиться:
Популярные книги

Последний Паладин. Том 2

Саваровский Роман
2. Путь Паладина
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Последний Паладин. Том 2

Потомок бога

Решетов Евгений Валерьевич
1. Локки
Фантастика:
попаданцы
альтернативная история
аниме
сказочная фантастика
5.00
рейтинг книги
Потомок бога

Сапер. Том II

Вязовский Алексей
2. Сапер
Фантастика:
альтернативная история
4.25
рейтинг книги
Сапер. Том II

Убивать чтобы жить 4

Бор Жорж
4. УЧЖ
Фантастика:
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 4

Я еще не князь. Книга XIV

Дрейк Сириус
14. Дорогой барон!
Фантастика:
юмористическое фэнтези
попаданцы
аниме
5.00
рейтинг книги
Я еще не князь. Книга XIV

Лекарь

Первухин Андрей Евгеньевич
1. Лекарь
Фантастика:
фэнтези
попаданцы
альтернативная история
7.50
рейтинг книги
Лекарь

История московских кладбищ. Под кровом вечной тишины

Рябинин Юрий Валерьевич
Научно-образовательная:
история
культурология
5.75
рейтинг книги
История московских кладбищ. Под кровом вечной тишины

Девочка из прошлого

Тоцка Тала
3. Айдаровы
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Девочка из прошлого

Я не бог. Книга XXXIV

Дрейк Сириус
34. Дорогой барон!
Фантастика:
юмористическое фэнтези
аниме
попаданцы
5.00
рейтинг книги
Я не бог. Книга XXXIV

Последний Герой. Том 3

Дамиров Рафаэль
3. Последний герой
Фантастика:
попаданцы
альтернативная история
фантастика: прочее
5.00
рейтинг книги
Последний Герой. Том 3

Шайтан Иван

Тен Эдуард
1. Шайтан Иван
Фантастика:
боевая фантастика
попаданцы
альтернативная история
5.00
рейтинг книги
Шайтан Иван

Война

Валериев Игорь
7. Ермак
Фантастика:
боевая фантастика
альтернативная история
5.25
рейтинг книги
Война

Дважды одаренный. Том VII

Тарс Элиан
7. Дважды одаренный
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Дважды одаренный. Том VII

Настольная книга по теологии. Библейский комментарий АСД Том 12

Церковь христиан адвентистов седьмого дня
Научно-образовательная:
религиоведение
5.00
рейтинг книги
Настольная книга по теологии. Библейский комментарий АСД Том 12