Вход/Регистрация
Эффективное использование STL
вернуться

Мейерс Скотт

Шрифт:

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

StringPtrLess
. Шаблон для таких функторов сравнения стоит держать под рукой. Пример:

struct DereferenceLess {

 template <typename PtrType>

 bool operator(PtrType pT1, // Параметры передаются по значению.

PtrType рТ2) const // поскольку они должны быть

 { // указателями (или по крайней мере

return *рТ1 < *рТ2; // вести себя, как указатели)

 }

};

Данный шаблон снимает необходимость в написании таких классов, как

StringPtrLess
, поскольку вместо них можно использовать
DereferenceLess
:

set<string*, DereferenceLess> ssp; // Ведет себя так же, как

// set<string*, stringPtrLess>

И последнее замечание. Данный совет посвящен ассоциативным контейнерам указателей, но он в равной степени относится и к контейнерам объектов, которые ведут себя как указатели (например, умные указатели и итераторы). Если у вас имеется ассоциативный контейнер умных указателей или итераторов, подумайте, не стоит ли задать тип сравнения и для него. К счастью, решение, приведенное для указателей, работает и для объектов-аналогов. Если определение

DereferenceLess
подходит в качестве типа сравнения для ассоциативного контейнера
T*
, оно с большой вероятностью подойдет и для контейнеров итераторов и умных указателей на объекты
T
.

Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства

Сейчас я покажу вам нечто любопытное. Создайте контейнер

set
с типом сравнения
less_equal
и вставьте в него число 10:

set<int, less_equal<int> > s; // Контейнер s сортируется по критерию "<="

s.insert(10); // Вставка числа 10

Теперь попробуйте вставить число 10 повторно:

s.insert(10);

При этом вызове

insert
контейнер должен выяснить, присутствует ли в нем число 10. Мы знаем, что такое число уже есть, но контейнер глуп как пробка и все проверяет лично. Чтобы вам было проще понять, что при этом происходит, назовем первоначально вставленный экземпляр 10A, а новый экземпляр — 10B.

Контейнер перебирает свои внутренние структуры данных и ищет место для вставки 10B. В итоге ему придется проверить 10A и сравнить его с 10B. Для ассоциативного контейнера «сравнение» сводится к проверке эквивалентности (см. совет 19), поэтому контейнер проверяет эквивалентность объектов 10A и 10B. Естественно, при этой проверке используется функция сравнения контейнера

set
; в нашем примере это функция
operator<=
, поскольку мы задали функцию сравнения
less_equal
, a
less_equal
означает
operator<=
. Затем контейнер проверяет истинность следующего выражения:

!(10a<=10b)&&!(10b<=10a) // Проверка эквивалентности 10A и 10B

Оба значения, 10A и 10B, равны 10, поэтому условие 10A<=10B заведомо истинно. Аналогично истинно и условие 10B<=10A. Приведенное выше выражение упрощается до

!(true)&&!(true)
, то есть
false&&false
— результат равен
false
. Другими словами, контейнер приходит к выводу, что 10A и 10B не эквивалентны, и вставляет 10B в контейнер наряду с 10A. С технической точки зрения эта попытка приводит к непредсказуемым последствиям, но на практике в контейнере
set
появляются два экземпляра значения 10, а это означает утрату одного из важнейших свойств
set
. Передача типа сравнения
less_equal
привела к порче контейнера! Более того, любаяфункция сравнения, которая возвращает
true
для равных значений, приведет к тем же последствиям. Равные значения по определению неэквивалентны! Здорово, не правда ли?

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

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

Например, в совете 20 рассказано о том, как написать функцию сравнения для контейнеров указателей

string*
обеспечивающую автоматическую сортировку содержимого контейнера по значениям строк, а не указателей. Приведенная функция сравнения сортирует строки по возрастанию, но давайте предположим, что вам понадобилась функция для сортировки по убыванию. Естественно, вы возьмете существующий код и отредактируете его. Но если не проявить достаточной осторожности, у вас может получиться следующий результат (изменения выделены жирным шрифтом):

struct StringPtrGreater:

 public binary_function<const string*, // Жирным шрифтом выделены

 const string*, // изменения, внесенные в код

 bool> { // из совета 20.

// Внимание - приведенное решение

// не работает!

 bool operator(const string *ps1, const string *ps2) const {

  • Читать дальше
  • 1
  • ...
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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