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

1 ... 23 24 25 [ 26 ] 27 28 29 ... 71


ваться по значениям указателей. Вряд ли такой порядок сортировки вас устроит, поэтому почти всегда определяются классы-функторы, используемые в качестве типов сравнения.

Обратите внимание на термин тими сравнения . Возможно, вас интересует, зачем возиться с созданием функтора вместо того, чтобы просто написать функцию сравнения для контейнера set? Например, так:

bool stringPtrLessCconst string* psl. Предполагаемая функция сравнения

const string* ps2) для указателей string*. { сортируемых по содержимому строки

return *psl<*ps2:

set<string.stringPtrLess> ssp; Попытка использования stringPtrLess

в качестве функции сравнения ssp. Не компилируется!!!

Проблема заключается в том, что каждый из трех параметров шаблона set должен быть типом. К сожалению, stringPtrLess - не тип, а функция, поэтому попытка задать stringPtrLess в качестве функции сравнения set не компилируется. Контейнеру set не нужна функция; ему нужен тип, на основании которого можно создать функцию.

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

struct DereferenceLess { template <typenanie PtrType>

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

PtrType рТ2) const поскольку они должны быть { указателями (или по крайней мере

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

Данный шаблон снимает необходимость в написании таких классов, как StringPtrLess, поскольку вместо них можно использовать DereferenceLess:

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

set<string*,stringPtrLess>

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



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

Сейчас я покажу вам нечто любопытное. Создайте контейнер set с типом сравнения lessequal и вставьте в него число 10:

set<int.less equal<int> > s: Контейнер s сортируется по критерию <= s.insert(lO): Вставка числа 10

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

s.insert(lO):

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

Контейнер перебирает свои внутренние структуры данных и ищет место для вставки 10в. В итоге ему придется проверить Юд и сравнить его с 10в. Для ассоциативного контейнера сравнение сводится к проверке эквивалентности (см. совет 19), поэтому контейнер проверяет эквивалентность объектов Юд и 10в. Естественно, при этой проверке используется функция сравнения контейнера set; в нащем примере это функция operator<=, поскольку мы задали функцию сравнения less equal, а less equal означает operator<=. Затем контейнер проверяет истинность следующего выражения:

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

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

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

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



осторожности, у вас может ползиться следующий результат (изменения выделены жирным шрифтом):

struct StringPtrGreater: public binary function<const string*. Мирным шрифтом выделены

const string*. изменения, внесенные в код Ьоо1> { из совета 20.

Внимание - приведенное решение не работает!

bool operatorO (const string *psl, const string *ps2) const {

return !(*psl<*ps2): Простое логическое отрицание

} старого условия не работает!

Идея заключается в том, чтобы изменить порядок сортировки логическим отрицанием условия в функции сравнения. К сожалению, отрицанием операции < является не > , а >= , а мы выяснили, что операция >= , возвращающая true для равных значений, не подходит для функции сравнения в ассоциативных контейнерах.

Правильный тип сравнения должен выглядеть так:

struct StringPtrGreater: Правильный тип сравнения

public binary function<const string*, для ассоциативных контейнеров const string*, bool> {

bool operatorO (const string *psl, const string *ps2) const {

return *ps2<*psl; Поменять местами операнды

Чтобы не попасть в ловушку, достаточно запомнить, что возвращаемое значение функции сравнения указывает, должно ли одно значение предшествовать другому в порядке сортировки, определяемом этой функцией. Равные значения никогда не предшествуют друг другу, поэтому функция сравнения всегда должна возвращать для них fal se.

Я знаю, о чем вы думаете. Конечно, это имеет смысл для set и тар, поскольку эти контейнеры не могут содержать дубликатов. А как насчет multiset и multimap? Раз эти контейнеры могут содержать дубликаты, так ли важно, что два объекта с одинаковыми значениями окажутся не эквивалентными? Сохраним оба, для этого и нужны multi-контейнеры. Верно?

Нет, неверно. Давайте вернемся к исходному примеру, но на этот раз воспользуемся контейнером multiset:

multiset<int,less equal<int> > s; lis сортируется по критерию <= s.insert(lO): Вставка числа 10а

s.insert(lO): Вставка числа 10в

Теперь S содержит два экземпляра числа 10, и было бы логично предположить, что при вызове equal range мы ползшим пару итераторов, описывающих интервал с обеими копиями. Однако это невозможно. Функция equalrange, несмотря паевое



1 ... 23 24 25 [ 26 ] 27 28 29 ... 71

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