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

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


название, определяет интервал не равных, а эквивалентных значений. В нашем примере функция сравнения s говорит, что Юд и 10в не эквивалентны, поэтому они не могут оказаться в интервале, определяемом функцией equal range.

Ну что, убедились? Функция сравнения всегда должна возвращать f al se для равных величин, в противном случае нарушается работа всех стандартных ассоциативных контейнеров (независимо от того, могут они содержать дубликаты или нет).

Строго говоря, функции сравнения, используемые для сортировки ассоциативных контейнеров, должны определять для сравниваемых объектов порядок строгой квазиупорядоченности (strict weak ordering); аналогичное ограничение действует и для функций сравнения, передаваемых алгоритмам, - таким, как sort (см. совет 31). Если вас заинтересуют подробности того; что понимается под строгой квазиупорядоченностью, информацию можно найти во многих серьезных руководствах по STL, в том числе The С++ Standard Library* [3], Generic Programming and the STL [4] и на web-сайте SGL посвященном STL [21]. Особых откровений не ждите, но одно из требований строгой квазиупорядоченности относится непосредственно к данному совету. Требование заключается в следующем: функция, определяющая строгую квазиупорядоченность, должна возвращать f al se при получении двух копий одного значения.

Совет 22. Избегайте изменения ключа на месте в контейнерах set и multiset

Понять смысл этого совета нетрудно. Контейнеры set/mul ti set, как и все стандартные ассоциативные контейнеры, хранят свои элементы в отсортированном порядке, и правильное поведение этих контейнеров зависит от сохранения этого порядка. Если изменить значение элемента в ассоциативном контейнере (например заменить 10 на 1000), новое значение окажется в неправильной позиции, что нарушит порядок сортировки элементов в контейнере.

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

map<int.string> m:

m.begin()->first = 10: Ошибка! Изменение ключей

в контейнере тар запрещено

multimap<int.string> mm:

rim.begin()->first = 20: Ошибка! Изменение ключей

в контейнере multimap запрещено

Дело в том, что элементы объекта типа map<K,V> или multimap<K,V> относятся к типу ра i r<const К. V>. Ключ относится к типу const К и поэтому не может изменяться. Впрочем, его все же можно изменить с применением const cast, как показано ниже. Хотите - верьте, хотите - нет, но иногда это даже нужно.



EmpIDSet::iterator i=se.find(selectedID): if (i!-se.endO) { i->setTitle( Corporate Deity ); Изменить должность

Обратите внимание: в заголовке этого совета ничего не сказано о контейнерах пир и multimap. Для этого есть веские причины. Как показывает предыдущий пример, модификация ключа на месте невозможна для тар и multimap (без применения преобразования const cast), но может быть допустима для set и multiset. Для объектов типа set<T> и multiset<T> в контейнере хранятся элементы типаТ, а не const Т. Следовательно, элементы контейнеров set и multiset можно изменять в любое время, и преобразование const cast для этого не требуется (вообще говоря, дело обстоит не так просто, но не будем забегать вперед).

Сначала выясним, почему элементы set и mul ti set не имеют атрибута const. Допустим, у нас имеется класс Employee:

class Employee { public:

const strings nameO const; Возвращает имя работника

void setNameCconst strings name); Задает имя работника

const strings titleO const; Возвращает должность

void setTitleCconst strings title): Задает должность

int idNumberO const: Возвращает код работника

Объект содержит разнообразные сведения о работнике. Каждому работнику назначается уникальный код, возвращаемый функцией idNumber. При создании контейнера set с объектами Empl оуее было бы вполне разумно упорядочить его по кодам работников:

struct IDNumberLess: public binary function<Employee.Employee.bool> { См. совет 40

bool operatorO (const Employees Ihs,

const Employees rhs) const

return 1 hs. idNumber О <rhs. idNumberO:

typedef set<Employee.IDNumberLess> EmplIDSet:

EmplIDSet se; Контейнер set объектов

Employee, упорядоченных no коду

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

Employee selectedID: Объект работника с заданным кодом



Совет 22 9 5

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

Спрашивается, почему нельзя применить ту же логику к ключам контейнеров тар и multimap? Почему бы не создать контейнер тар, ассоциирующий работников со страной, в которой они живут; контейнер с функцией сравнения IDNumberLess, как в предыдущем примере? И почему бы в таком контейнере не изменить должность без изменения кода работника, как в предыдущем примере?

Откровенно говоря, мне это кажется вполне логичным, однако мое личное мнение в данном слзае несущественно. Важно то, что Комитет по стандартизации решил, что ключи map/mul timap должны быть неизменными (const), а значения set/ multiset - не должны.

Значения в контейнерах set/multiset не являются неизменными, поэтому попытки их изменения обычно нормально компилируются. Данный совет лишь напоминает вам о том, что при модификации элементов set/multiset не следует изменять ключевую часть (то есть ту часть элемента, которая влияет на порядок сортировки в контейнере). В противном случае целостность данных контейнера будет нарушена, операции с контейнером начнут приводить к непредсказуемым результатам, и все это произойдет по вашей вине. С другой стороны, это ограничение относится только к ключевым атрибутам объектов, содержащихся в контейнере. Остальные атрибуты объектов находятся в вашем полном распоряжении - изменяйте на здоровье!

Впрочем, не все так просто. Хотя элементы set/multiset и не являются неизменными, реализации могут предотвратить их возможную модификацию. Например, оператор * для set<T>:: iterator может возвращать const Т&, то есть результат разыменования итератора set может быть ссылкой на const-элемент контейнера! При такой реализации изменение элементов set и multiset невозможно, поскольку при любом обращении к элементу автоматически добавляется объявление const.

Законны ли такие реализации? Может, да, а может - нет. По этому вопросу Стандарт высказывается недостаточно четко, и в соответствии с законом Мерфи разные авторы интерпретируют его по-разному. В результате достаточно часто встречаются реализации STL, в которых следующий фрагмент компилироваться не будет (хотя ранее говорилось о том, что он успешно компилируется):

EmplIDSet se; Контейнер set объектов

Employee, упорядоченных по коду

Employee selectedID; Объект работника с заданным кодом

EnpIDSet:;iterator i=se.find(selectedID); if (i!=se.end()) {

i->setTitle( Corporate Deity ); Некоторые реализации STL } выдают ошибку в этой строке

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



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

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