Программирование >>  Включение нужных заголовков 

1 ... 10 11 12 [ 13 ] 14 15 16 ... 71


Для ассоциативных контейнеров правильным решением будет вызов erase:

c.erase(1963); Функция erase обеспечивает оптимальное

удаление элементов с заданным значением из стандартных ассоциативных контейнеров

Функция erase не только справляется с задачей, но и эффективно решает ее с логарифмической сложностью (вызовы гепюуе в последовательных контейнерах обрабатываются с линейной сложностью). Более того, в ассоциативных контейнерах функция erase обладает дополнительным преимуществом - она основана на проверке эквивалентности вместо равенства (это важное различие рассматривается в совете 19).

Слегка изменим проблему. Вместо того чтобы удалять из с все объекты с заданным значением, давайте удалим все объекты, для которых следующий предикат (совет 39) возвращает true:

bool badValuednt х): Возвращает true для удаляемых объектов

В последовательных контейнерах (vector, string, deque и 1 ist) достаточно заменить renrove на remove i f:

c.erase(reniove if(c.begin().c.end().badValue). Лучший способ уничтожения

c.endO): объектов, для которых badValue

возвращает true, в контейнерах vector, string и deque

с.remove if(badValue); Оптимальный способ уничтожения

объектов, для которых badValue возвращает true, в контейнере list

Со стандартными ассоциативными контейнерами дело обстоит посложнее. Существуют два решения: одно проще программируется, другое эффективнее работает. В первом решении нужные значения копируются в новый контейнер функцией remove copy, после чего содержимое двух контейнеров меняется местами:

АссоцКонтейнер<Ш> с; II с - один из стандартных

ассоциативных контейнеров

АссоцКонтейнер<лпХ> goodValues; Временный контейнер для хранения

элементов, оставшихся после удаления

reniove copy if(c.begin(),C.endO, Скопировать оставшиеся элементы inserterCgoodValues, из с в goodValues

goodValues.endO), badValue);

с.swap(goodValues); Поменять содержимое с и goodValues

У подобного решения имеется недостаток - необходимость копирования элементов, остающихся после удаления. Такое копирование может обойтись дороже, чем нам хотелось бы.

От этих затрат можно избавиться за счет непосредственного удаления элементов из исходного контейнера. Но поскольку в ассоциативных контейнерах отсутствует функция, аналогичная removei f, придется перебирать все элементы с в цикле и принимать решение об удалении текущего элемента.



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

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

for(/4ccofy/<OHre/5Hep<int>; iiterator i=c.begin(): Наглядный, бесхитростный i!=c.end(): и ошибочный код, который

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

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

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

We поступайте так!

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

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

АссоцКонтейиер<лх\1> с:

ог{АссоцКонтейнер<Ш>::Мегд,1ог i=c.begin(): Третья часть заголовка i!=c.end(): цикла for пуста; i теперь

/* пусто */) { изменяется внутри цикла

if (badValue(*i)) c.erase(1++); Для удаляемых элементов

else ++i; передать erase текущее

} значение i и увеличить i.

Для остающихся элементов просто увеличить i

Новый вариант вызова erase работает, поскольку выражение 1++ равно старому значению i, но у него имеется побочный эффект - приращение i. Таким образом, мы передаем старое (не увеличенное) значение i и увеличиваем i перед вызовом erase. Именно это нам и требовалось. Хотя это решение выглядит просто, лишь немногие программисты предложат его с первой попытки.

Пора сделать следующий шаг. Помимо простого удаления всех элементов, для которых badValue возвращает true, мы также хотим регистрировать каждую операцию удаления в журнале.

Для ассоциативных контейнеров задача решается очень просто, поскольку она требует лишь тривиальной модификации созданного цикла:

ofstream logFile; Файл журнала

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

forMccofy/<OHTe/?Hep<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 нельзя заставить вывести данные в журнал. Более того, вариант с циклом for, только что продемонстрированный для ассоциативных контейнеров, тоже не подходит, поскольку для контейнеров vector, string и deque он приведет к непредсказуемым последствиям. Вспомните, что для этих контейнеров в результате вызова erase становятся недействительными все итераторы, указывающие на удаляемый элемент. Кроме того, недействительными становятся все итераторы после удаляемого элемента, в нашем примере - все итераторы после i. Конструкции вида i-н-, -н-i и т. д. невозможны, поскольку ни один из ползенных итераторов не будет действительным.

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

for (/7ocfl/<OHre(5Hep<int>::iterator=c.begin(); i!=c.end():){

if (badValue(*i)) {

logFile Erasing *i \n;

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

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

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

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

Какое из этих решений лучше подойдет для контейнера list? Оказывается, в отношении перебора и удаления 1 i st может интерпретироваться как vector/ stri ng/deque или как ассоциативный контейнер - годятся оба способа. Обычно выбирается первый вариант, поскольку 1 ist, как и vector/string/deque, принадлежит к числу последовательных контейнеров. С точки зрения опытного программиста STL программа, в которой перебор и удаление из 1 i st производятся по правилам ассоциативных контейнеров, выглядит странно.

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

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

контейнеры vector, stri ng и deque: используйте идиому erase/remove;

контейнер 1 i st: используйте 1 i st:: remove;

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



1 ... 10 11 12 [ 13 ] 14 15 16 ... 71

© 2006 - 2024 pmbk.ru. Генерация страницы: 0.001
При копировании материалов приветствуются ссылки.
Яндекс.Метрика