Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели
Совет 20. Определите тип сравнения для ассоциативного контейнера, содержащего указатели
Предположим, у нас имеется контейнер set, содержащий указатели string*, и мы пытаемся включить в него несколько новых элементов:
set<string*> ssp; // ssp = "set of string ptrs"
ssp.insert(new string("Anteater"));
ssp.insert(new string("Wombat"));
ssp.insert(new string("Lemur"));
ssp.insert(new string("Penguin"));
Следующий фрагмент выводит содержимое set. Предполагается, что строки будут выведены в алфавитном порядке — ведь содержимое контейнеров set автоматически сортируется!
for (set<string*>::const_iterator i = ssp.begin(); // Предполагаемый
i!=ssp.end();
++i)
cout <<*i << endl;
Однако на практике ничего похожего не происходит. Вместо строк выводятся четыре шестнадцатеричных числа — значения указателей. Поскольку в контейнере set хранятся указатели, *i является не строкой, а указателем на строку. Пусть этот урок напоминает, чтобы вы следовали рекомендациям совета 43 и избегали написания собственных циклов. Использование алгоритма сору:
copy(ssp.begin(),ssp.end(),// Скопировать строки.
ostream_iterator<string>(cout," ")); //содержащиеся в ssp. в cout
//(не компилируется!)
не только делает программу более компактной, но и помогает быстрее обнаружить ошибку, поскольку вызов сору не компилируется. Итератор ostream_iterator должен знать тип выводимого объекта, поэтому когда компилятор обнаруживает расхождение между заданным в параметре шаблона типом string и типом объекта, хранящегося в ssp (string*), он выдает ошибку. Еще один довод в пользу сильной типизации...
Если заменить *i в цикле на **i, возможно, вы получите нужный результат — но скорее всего, этого не произойдет. Да, строки будут выведены, но вероятность их следования в алфавитном порядке равна всего 1 /24. Контейнер ssp хранит свои элементы в отсортированном виде, однако он содержит указатели, поэтому сортироваться будут значения указателей, а не строки. Существует 24 возможных перестановки для четырех указателей, то есть 24 разных последовательности, из которых лишь одна отсортирована в алфавитном порядке[2].
Подходя к решению этой проблемы, нелишне вспомнить, что объявление
set<string*> ssp;
представляет собой сокращенную запись для объявления
set<string*.less<string*> > ssp;
Строго говоря, это сокращенная запись для объявления
set<string*.less<string*>.allocator<string*> > ssp;
но в контексте данного совета распределители памяти несущественны.
Если вы хотите сохранить указатели string* в контейнере set так, чтобы их порядок определялся значениями строк, стандартный функтор сравнения less<string*> вам не подойдет. Вместо этого необходимо написать собственный функтор сравнения, который получает указатели string* и упорядочивает их по содержимому строк, на которые они ссылаются. Пример:
struct StringPtrLess:
public binary_function<const string*, // Базовый класс
const string*, // описан в совете 40
bool> {
bool operator() (const string *ps1, const string *ps2) const
{
return *ps1<*ps2:
}
};.
После этого StringPtrLess используется в качестве типа критерия сравнения ssp:
typedef set<string*, StringPtrLess> StringPtrSet;
StringPtrSet ssp; // Создать множество с объектами string
// и порядком сортировки, определяемым
// критерием StringPtrLess
// Вставить те же четыре строки
Теперь приведенный выше цикл будет работать именно так, как предполагалось (при условии, что ошибка была исправлена и вместо *i используется **i).
for(StringPtrSet::const _iterator i = ssp.begin();
i != ssp.end();// Порядок вывода:
++i) // "Anteater", "Lemur",
cout«**i«endl; // "Pengun". "Wombat"
Если вы предпочитаете использовать алгоритм, напишите функцию, которая разыменовывает указатели string* перед выводом, а затем используйте ее в сочетании с for_each:
void print(const string *ps)// Вывести в cout объект.
{// на который ссылается ps
cout «*ps « endl;
}
for_each(ssp.begin(),ssp.end(),print); // Вызвать print для каждого
// элемента ssp
Существует более изощренное решение — обобщенный функтор разыменования, используемый с transform и ostream_iterator:
// Функтор получает Т* и возвращает const Т&
struct Dereference{
template<typename T>
const T& operator() (const T* ptr) const
{
return *ptr;
}
};
transform(ssp.begin(),ssp.end(),// "Преобразовать" каждый
ostream.iterator<string>(cout," "). // элемент ssp посредством
Dereference());// разыменования и записать
// результаты в cout
Впрочем, замена циклов алгоритмами будет подробно рассматриваться позднее, в совете 43. А сейчас речь идет о том, что при создании стандартного ассоциативного контейнера указателей следует помнить: содержимое контейнера будет сортироваться по значениям указателей. Вряд ли такой порядок сортировки вас устроит, поэтому почти всегда определяются классы-функторы, используемые в качестве типов сравнения.
Обратите внимание на термин «тип сравнения». Возможно, вас интересует, зачем возиться с созданием функтора вместо того, чтобы просто написать функцию сравнения для контейнера set? Например, так:
bool stringPtrLess(const string* psl, // Предполагаемая функция сравнения
const string* ps2) // для указателей string*.
{ // сортируемых по содержимому строки
return *psl<*ps2:
}
set<string.stringPtrLess> ssp; // Попытка использования stringPtrLess
// в качестве функции сравнения ssp.
// Не компилируется!!!
Проблема заключается в том, что каждый из трех параметров шаблона set должен быть типом. К сожалению, stringPtrLess — не тип, а функция, поэтому попытка задать stringPtrLess в качестве функции сравнения set не компилируется. Контейнеру set не нужна функция; ему нужен тип, на основании которого можно создать функцию.
Каждый раз, когда вы создаете ассоциативный контейнер указателей, помните о том, что вам, возможно, придется задать тип сравнения контейнера. В большинстве случаев тип сравнения сводится к разыменованию указателя и сравнению объектов, как это сделано в приведенном выше примере StringPtrLess. Шаблон для таких функторов сравнения стоит держать под рукой. Пример:
struct DereferenceLess {
template <typename PtrType>
bool operator()(PtrType pTl, // Параметры передаются по значению.
PtrType рТ2) const // поскольку они должны быть
{ // указателями (или по крайней мере
return *рТ1<*рТ2:// вести себя, как указатели)
}
};
Данный шаблон снимает необходимость в написании таких классов, как StringPtrLess, поскольку вместо них можно использовать DereferenceLess:
set<string*.DereferenceLess> ssp; // Ведет себя так же. как
// set<string*,stringPtrLess>
И последнее замечание. Данный совет посвящен ассоциативным контейнерам указателей, но он в равной степени относится и к контейнерам объектов, которые ведут себя как указатели (например, умные указатели и итераторы). Если у вас имеется ассоциативный контейнер умных указателей или итераторов, подумайте, не стоит ли задать тип сравнения и для него. К счастью, решение, приведенное для указателей, работает и для объектов-аналогов. Если определение DereferenceLess подходит в качестве типа сравнения для ассоциативного контейнера Т*, оно с большой вероятностью подойдет и для контейнеров итераторов и умных указателей на объекты Т.
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Создание контейнера с Web-формой поиска
Создание контейнера с Web-формой поиска Откроем Web-страницу index.htm в Блокноте, найдем созданный в главе 20 фрагмент кода, создающий Web-форму поиска, и удалим его. Вместо него мы вставим сразу после открывающего тега <BODY> код, приведенный в листинге 21.4. Листинг 21.4 <DIV
Свойства текста, содержащего иероглифы
Свойства текста, содержащего иероглифы text-justifyЗадает тип текста по ширине. Значение атрибута text-align при этом должно быть равно justify.text-justify: auto|newspaper|distribute|distribute-all-lines|distribute-center-last|inter-word|inter-ideograph|inter-cluster|kashida;Поддерживается IE начиная с 5.0text-autospaceПозволяет установить, будет ли
Создание контейнера с Web-формой поиска
Создание контейнера с Web-формой поиска Откроем Web-страницу index.htm в Блокноте, найдем созданный в главе 20 фрагмент кода, создающий Web-форму поиска, и удалим его. Вместо него мы вставим сразу после открывающего тега <BODY> код, приведенный в листинге 21.4. Листинг 21.4 <DIV
Определите стратегию продаж на сайте
Определите стратегию продаж на сайте Ваш сайт в Интернете следует рассматривать в первую очередь как место, где совершаются продажи. Все приемы увеличения продаж, которые вы применяете во время общения с покупателем, вы можете применить и на своем сайте.В связи с этим
Определите целевые действия, которые должен совершить посетитель на сайте
Определите целевые действия, которые должен совершить посетитель на сайте Вспомните ситуацию, когда вы заходили на какой-нибудь сайт и абсолютно не понимали, с чего начать его просмотр и что вам делать дальше.Вроде бы сайт ничем не отличается от аналогов, на нем есть
69. Определите разумную стратегию обработки ошибок и строго ей следуйте
69. Определите разумную стратегию обработки ошибок и строго ей следуйте РезюмеЕще на ранней стадии проектирования разработайте практичную, последовательную и разумную стратегию обработки ошибок и строго следуйте ей. Убедитесь, что ваша стратегия включает
7.1. Перебор элементов контейнера
7.1. Перебор элементов контейнера ПроблемаИмеется диапазон итераторов — скорее всего, из стандартного контейнера — и стандартные алгоритмы не удовлетворяют вашим требованиям, так что вам требуется выполнить итерации самостоятельно.РешениеДля доступа к элементам
7.2. Удаление объектов из контейнера
7.2. Удаление объектов из контейнера ПроблемаТребуется удалить объекты из контейнера.РешениеДля удаления одного или диапазона элементов используйте метод контейнера erase или один из стандартных алгоритмов. Пример 7.2 показывает пару различных способов удаления элементов
11.3. Вычисление суммы и среднего значения элементов контейнера
11.3. Вычисление суммы и среднего значения элементов контейнера ПроблемаТребуется вычислить сумму и среднее значение чисел, содержащихся в контейнере.РешениеДля расчета суммы можно использовать функцию accumulate из заголовочного файла <numeric> и затем разделить ее на
11.7. Инициализация контейнера случайными числами
11.7. Инициализация контейнера случайными числами ПроблемаТребуется заполнить произвольный контейнер случайными числами.РешениеМожно использовать функции generate и generate_n из заголовочного файла <algorithm> совместно с функтором, возвращающим случайные числа. Пример 11.13
Совет 1. Внимательно подходите к выбору контейнера
Совет 1. Внимательно подходите к выбору контейнера •Стандартные последовательные контейнеры STL: vector, string, deque и list.•Стандартные ассоциативные контейнеры STL: set, multiset, map и multimap.•Нестандартные последовательные контейнеры: slist и rope. Контейнер slist представляет односвязный
Совет 4. Вызывайте empty вместо сравнения size() с нулем
Совет 4. Вызывайте empty вместо сравнения size() с нулем Для произвольного контейнера с следующие две команды фактически эквивалентны:if (c.size()==0)...if (c.empty())...Возникает вопрос — почему же предпочтение отдается одной конструкции, особенно если учесть, что empty обычно реализуется в
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера
Совет 7. При использовании контейнеров указателей, для которых вызывался оператор new, не забудьте вызвать delete для указателей перед уничтожением контейнера Контейнеры STL отличаются умом и сообразительностью. Они поддерживают итераторы для перебора как в прямом, так и в
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства
Совет 21. Следите за тем, чтобы функции сравнения возвращали false в случае равенства Сейчас я покажу вам нечто любопытное. Создайте контейнер set с типом сравнения less_equal и вставьте в него число 10:set<int,less_equal<int> > s; // Контейнер s сортируется по критерию "<="s.insert(10); // Вставка
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare
Совет 35. Реализуйте простые сравнения строк без учета регистра символов с использованием mismatch или lexicographical_compare Один из вопросов, часто задаваемых новичками в STL — «Как в STL сравниваются строки без учета регистра символов?» Простота этого вопроса обманчива. Сравнения
5.3.2. Создание ассоциативного чертежа опоры
5.3.2. Создание ассоциативного чертежа опоры Создание чертежаВыполните команду Файл | Создать | Чертеж.Создание стандартных видов1. Выполните команду Вставка | Вид с модели | Стандартные.2. Откройте документ Опора.3. На вкладке Параметры Панели свойств в поле Ориентация