Книги
чёрным по белому
Главное меню
Главная О нас Добавить материал Поиск по сайту Карта книг Карта сайта
Книги
Археология Архитектура Бизнес Биология Ветеринария Военная промышленность География Геология Гороскоп Дизайн Журналы Инженерия Информационные ресурсы Искусство История Компьютерная литература Криптология Кулинария Культура Лингвистика Математика Медицина Менеджмент Металлургия Минералогия Музыка Научная литература Нумизматика Образование Охота Педагогика Политика Промышленные производства Психология Путеводители Религия Рыбалка Садоводство Саморазвитие Семиотика Социология Спорт Столярное дело Строительство Техника Туризм Фантастика Физика Футурология Химия Художественная литература Экология Экономика Электроника Энергетика Этика Юриспруденция
Новые книги
Цуканов Б.И. "Время в психике человека" (Медицина)

Суворов С. "Танк Т-64. Первенец танков 2-го поколения " (Военная промышленность)

Нестеров В.А. "Основы проэктирования ракет класса воздух- воздух и авиационных катапульных установок для них" (Военная промышленность)

Фогль Б. "101 вопрос, который задала бы ваша кошка своему ветеринару если бы умела говорить" (Ветеринария)

Яблоков Н.П. "Криминалистика" (Юриспруденция)
Реклама

Эффективное использование STL. Библиотека программиста - Мейерс С.

Мейерс С. Эффективное использование STL. Библиотека программиста — Спб.: Питер , 2002. — 224 c.
ISBN 5-94723-382-7
Скачать (прямая ссылка): effektivnoeispolzovaniestlbibliote2002.djvu
Предыдущая << 1 .. 73 74 75 76 77 78 < 79 > 80 81 82 83 84 85 .. 114 >> Следующая

зависит только от параметров. Если f - "чистая" функция, а х и у -
объекты, то возвращаемое значение f(x,y) может измениться только в случае
изменения х или у.
Совет 39 155
В C++ все данные, используемые "чистыми" функциями, либо передаются в
виде параметров, либо остаются постоянными на протяжении всего жизненного
цикла функции (естественно, такие постоянные данные объявляются с
ключевым словом const). Если бы данные, используемые "чистой" функцией,
могли изменяться между вызовами, то вызов этой функции в разные моменты
времени с одинаковыми параметрами мог бы давать разные результаты, что
противоречит определению "чистой" функции.
Из сказанного должно быть понятно, что нужно сделать, чтобы предикаты
были "чистыми" функциями. Мне остается лишь убедить читателя в том, что
эта рекомендация обоснована. Для этого придется ввести еще один термин,
• Предикатным классом называется класс функтора, у которого функция
operator() является предикатом, то есть возвращает true или fal se. Как и
следует ожидать, во всех случаях, когда STL ожидает получить предикат,
может передаваться либо настоящий предикат, либо объект предикатного
класса.
Обещаю, что новых терминов больше не будет. Теперь давайте разберемся,
почему следует выполнять рекомендацию данного совета.
В совете 38 объяснялось, что объекты функций передаются по значению,
поэтому при проектировании необходимо позаботиться о возможном
копировании. Для объектов функций, являющихся предикатами, существует и
другой аргумент в пользу специальной поддержки копирования. Алгоритмы
могут создавать копии функторов и хранить их определенное время перед
применением, причем некоторые реализации алгоритмов этим активно
пользуются. Важнейшим следствием этого факта является то, что предикатные
функции должны быть "чистыми".
Предположим, вы нарушили это ограничение. Ниже приведен плохо
спроектированный класс предиката, который независимо от переданных
аргументов возвращает true только один раз - при третьем вызове. Во всех
остальных случаях возвращается fal se.
class BadPredicate: // Базовый класс описан
public unary_function<Widget,bool>{ // в совете 40
public:
BadPredicate():timesCalles(0){} // Переменная timesCalled
// инициализируется нулем
bool operatorO (const WidgetS)
{
return ++timesCal led == 3;
}
private: size_t timesCalled:
}:
Предположим, класс BadPredicate используется для исключения третьего
объекта Widget из контейнера vector<Widget>:
vector<Widget> vw: // Создать вектор и заполнить его
// объектами Widget
vww.erase(removeJf(vw.begin(), // Удалить третий объект Widget.
vw.endO, // связь между erase и remove_if
BadPredicateO). // описана в совете 32
vw.endO);
156 Глава б • Функции, функторы и классы функций
Программа выглядит вполне разумно, однако во многих реализациях STL из
вектора vw удаляется не только третий, но и шестой элемент!
Чтобы понять, почему это происходит, необходимо рассмотреть один из
распространенных вариантов реализации remove_i f. Помните, что эта
реализация не является обязательной.
tempiate<typename Fwdlterator,typename Predicate>
Fwdlterator remove_if(FwdIterator begin, Fwdlterator end, Predicate p)
{
begin = find_if(begin,end,p);
if(begin==end) return begin: else {
Fwdlterator next=begin:
return remove_copy_i f(++next,end,begin,p);
}
}
Подробности нас сейчас не интересуют. Обратите внимание: предикат р
сначала передается f i nd_i f, а затем remove_copy_i f. Конечно, в обоих
случаях р передается по значению - то есть копируется (теоретически
возможны исключения, но на практике дело обстоит именно так; за
подробностями обращайтесь к совету 38).
Первый вызов remove_i f (расположенный в клиентском коде, удаляющем
третий элемент из vw) создает анонимный объект BadPredicate с внутренней
переменной ti mesCal 1 ed, равной 0. Этот объект, известный в remove_i f
под именем р, затем копируется в find_if, поэтому find_if тоже получает
объект BadPredicate с переменной timesCalled, равной 0. Алгоритм find_if
"вызывает" этот объект, пока тот не вернет true; таким образом, объект
вызывается три раза. Затем fi nd_i f возвращает управление remove_i f.
Remove_i f продолжает выполняться и в итоге вызывает removecopyi f,
передавая в качестве предиката очередную копию р. Но переменная
timesCalled объекта р по-прежнему равна 0! Ведь алгоритм find_if вызывал
не р, а лишь копию р. В результате при третьем вызове из remove_copy_i f
предикат тоже вернет true. Теперь понятно, почему remove_if удаляет два
объекта Widget вместо одного.
Чтобы обойти эту лингвистическую ловушку, проще всего объявить функцию
operator () с ключевым словом const в предикатном классе. В этом случае
компилятор не позволит изменить переменные класса:
class BadPredicate: public unary_function<Widget.bool> {
public:
bool operatorO(const WidgetS) const {
return ++timesCalled == 3: // Ошибка! Изменение локальных данных
} // в константной функции невозможно
Из-за простоты этого решения я чуть было не озаглавил этот совет
Предыдущая << 1 .. 73 74 75 76 77 78 < 79 > 80 81 82 83 84 85 .. 114 >> Следующая