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

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


Напомню, что функция сортировки эти строки не различает. Следует ли вставить строки в произвольном порядке, добровольно отказавшись от детерминированного порядка перебора содержимого контейнера? Недетерминированный порядок перебора уже присущ ассоциативным контейнерам multiset и multimap, поскольку Стандарт не устанавливает никаких ограничений на относительный порядок следования эквивалентных значений (multiset) или ключей (multimap). Или нам следует настоять на детерминированном порядке содержимого s и проигнорировать вторую попытку вставки (для строки persephone )? А если будет выбран этот вариант, что произойдет при выполнении следующей команды:

if (s.findCpersephdne ) != s.endO)... Каким будет результат проверки?

Функция find использует проверку равенства, но если проигнорировать второй вызов insert для сохранения детерминированного порядка элементов s, проверка даст отрицательный результат - хотя строка persephone была отвергнута как дубликат!

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

Но стоит отойти от сортированных ассоциативных контейнеров, как ситуация изменяется, и проблему равенства и эквивалентности приходится решать заново. Существуют две общепринятые реализации для нестандартных (но широко распространенных) ассоциативных контейнеров на базе хэш-таблиц. Одна реализация основана на равенстве, а другая - на эквивалентности. В совете 25 приводится дополнительная информация об этих контейнерах и тех принципах, на которых они основаны.

Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели

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

set<string*> ssp: ssp = set of string ptrs

ssp.i nsert(new stri ng( Anteater )): ssp.insert(new string( Wombat )): ssp.insert(new stri ng( Lemur )): ssp.insert(new string( Penguin )):



Строго говоря, не все 24 перестановки равновероятны, так что вероятность 1/24 не совсем точна. Тем не менее, остается бесспорный факт: а/ществуют 24 разные перестановки, и вы можете получить любую из них.

Следующий фрагмент выводит содержимое set. Предполагается, что строки будут выведены в алфавитном порядке - ведь содержимое контейнеров set автоматически сортируется!

for (set<string*>: iconstjterator i = ssp.beginO: Предполагаемый i != ssp.endO; порядок вывода:

++i) Anteater . Lemur ,

cout *i endl: Penguin , Wombat

Однако на практике ничего похожего не происходит. Вместо строк выводятся четыре шестнадцатеричных числа - значения указателей. Поскольку в контейнере set хранятся указатели, *i является не строкой, а указателем на строку. Пусть этот урок напоминает, чтобы вы следовали рекомендациям совета 43 и избегали написания собственных циклов. Использование алгоритма сору:

copyCssp.beginO.ssp.endO, Скопировать строки,

ostream iterator<string>(cout, \n )): содержащиеся в ssp, в cout

(не компилируется!)

не только делает программу более компактной, но и помогает быстрее обнаружить ошибку, поскольку вызов сору не компилируется. Итератор ostreain i terator должен знать тип выводимого объекта, поэтому когда компилятор обнаруживает расхождение между заданным в параметре шаблона типом string и типом объекта, хранящегося в ssp (string*), он вьщает ошибку. Еще один довод в пользу сильной типизации...

Если заменить *i в цикле на **i, возможно, вы получите нужный результат - но скорее всего, этого не произойдет. Да, строки будут выведены, но вероятность их следования в алфавитном порядке равна всего 1 /24. Контейнер ssp хранит свои элементы в отсортированном виде, однако он содержит указатели, поэтому сортироваться будут значения указателей, а не строки. Существует 24 возможных перестановки для четырех указателей, то есть 24 разных последовательности, из которых лишь одна отсортирована в алфавитном порядке.

Подходя к решению этой проблемы, нелишне вспомнить, что объявление

set<string*> ssp: представляет собой сокращенную запись для объявления set<string*.1ess<string*> > ssp:

Строго говоря, это сокращенная запись для объявления

set<string*.less<string*>,a11ocator<string*> > ssp:

но в контексте данного совета распределители памяти несущественны.

Если вы хотите сохранить указатели stri ng* в контейнере set так, чтобы их порядок определялся значениями строк, стандартный функтор сравнения less<string*> вам не подойдет. Вместо этого необходимо написать собственный функтор сравнения, который получает указатели string* и упорядочивает их по содержимому строк, на которые они ссылаются. Пример:



struct StringPtrLess: public binary function<const string*. Базовый класс

const string*, описан в совете 40 bool> {

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

return *psl<*ps2:

После этого Stri ngPtrLess используется в качестве типа критерия сравнения ssp: typedef set<string*, StringPtrLess> StringPtrSet:

StringPtrSet ssp; Создать множество с объектами string

и порядком сортировки, определяемым критерием StringPtrLess Вставить те же четыре строки

Теперь приведенный выше цикл будет работать именно так, как предполагалось (при условии, что ошибка была исправлена и вместо *i используется **i).

forCStringPtrSet: :const Jterator i= ssp.beginO;

i != ssp.endO; Порядок вывода:

++i) Anteater . Lemur ,

cout **i endl: Penguin . Wombat

Если вы предпочитаете использовать алгоритм, напишите функцию, которая разыменовывает указатели string* перед выводом, а затем используйте ее в сочетании с for each:

void print(const string *ps) Вывести в cout объект,

{ на который ссылается ps

cout *ps endl:

for each(ssp.begin(),ssp.endO.print); Вызвать print для каждого

элемента ssp

Существует более изощренное решение - обобщенный функтор разыменования, используемый с transform и ostreain i terator:

Функтор получает Т* и возвращает const Т& struct Dereference!

tempiate<typename Т>

const Т& operatorO (const T* ptr) const

return *ptr:

transformCssp.beginO,ssp.endO. Преобразовать каждый

ostream.iterator<string>(cout. \n ), элемент ssp посредством DereferenceO): разыменования и записать

результаты в cout

Впрочем, замена циклов алгоритмами будет подробно рассматриваться позднее, в совете 43. А сейчас речь идет о том, что при создании стандартного ассоциативного контейнера указателей следует помнить: содержимое контейнера будет сортиро-



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

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