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

1 ... 54 55 56 [ 57 ] 58 59 60 ... 71


Тем не менее при подсчете объектов в ассоциативных контейнерах count надежно занимает свою нишу. В частности, вызов count предпочтительнее вызова equal range с последующим применением di stance к полученным итераторам. Во-первых, само название функции подсказывает ее смысл - слово count означает подсчет . Во-вторых, count упрощает работу программиста, поскольку ему не приходится создавать пару и передавать ее компоненты при вызове distance. В-третьих, count работает чуть быстрее.

Попробуем подвести итог всему, о чем говорилось в настоящем совете. Информация собрана в следующей таблице.

Алгоритм

Функция контейнера

Что вы хотите узнать

Несортированный

Сортированный

Для set

Для multiset

интервал

интервал

и map

и multimap

Присутствует ли заданное

find

binary search

count

find

значение?

Присутствует ли заданное

find

equaLrange

find

find или

значение? И если

lower bound

присутствует, то где

(CM. ранее)

находится первый объект

с этим значением?

Где находится первый

findjf

lower bound

lower

lower bound

объект со значением,

bound

не предшествующим

заданному?

Где находится первый

findjf

upper bound

upper

upper bound

объект со значением.

bound

следующим после

заданного?

Сколько объектов имеют

count

equaLrange

count

count

заданное значение?

Где находятся все

equaLrange

equaLrange

equaL

find

объекты с заданным

range

(итеративный

значением?

вызов)

Несколько странно выглядит частое присутствие equalrange в столбце, относящемся к сортированным интервалам. Оно связано с особой ролью проверки эквивалентности при поиске. Использование lower bound и upperbound чревато ошибочной проверкой равенства, а при использовании equal range более естественно выглядит проверка эквивалентности. Во второй строке предпочтение отдается equal range еще по одной причине: equal range работает с логарифмическим временем, а вызов find связан с линейными затратами времени.

Для контейнеров multiset и multimap в качестве возможных кандидатов для поиска первого объекта с заданным значением указаны два алгоритма, find и lower bound. Обычно для решения этой задачи выбирается find - возможно, вы обратили внимание, что именно этот алгоритм указан в таблице для контейнеров set и тар. Однако multi-контейнеры не гарантируют, что при наличии нескольких элементов с заданным значением find найдет первый элемент в контейнере; известно лишь то, что будет найден один из этих элементов. Если вы действительно хотите



найти первый объект с заданным значением, воспользуйтесь lower bound и выполните вручную вторую часть проверки эквивалентности, описанной в совете 19 (без этой проверки можно обойтись при помощи equal range, но вызов equal range обходится дороже, чем вызов lowerbound).

Выбрать между count, find, binary search, lower bound, upper bound и equal range несложно. Предпочтение отдается тому алгоритму или функции, которые обладают нужными возможностями, обеспечивают нужное быстродействие и требуют минимальных усилий при вызове. Следуйте этой рекомендации (или обращайтесь к таблице), и у вас никогда не будет проблем с выбором.

Совет 46. Передавайте алгоритмам объекты функций вместо функций

Часто говорят, что повышение уровня абстракции языков высокого уровня приводит к снижению эффективности сгенерированного кода. Александр Степанов, изобретатель STL, однажды разработал небольшой комплекс тестов для оценки платы за абстракцию при переходе с С на С++. В частности, результаты этих тестов показали, что код, сгенерированный для работы с классом, содержащим double, почти всегда уступает по эффективности соответствующему коду, непосредственно работающему с double. С учетом сказанного вас может удивить тот факт, что передача алгоритмам объектов функций STL - то есть объектов, маскирующихся под функции, - обычно обеспечивает более эффективный код, чем передача настоящих функций.

Предположим, вы хотите отсортировать вектор чисел типа doubl е по убыванию. Простейшее решение этой задачи средствами STL основано на использовании алгоритма sort с объектом функции типа greater<doubl е>:

vector<double> v:

sort(V.begi n().V.end(),greater<double>()):

Вспомнив о плате за абстракцию , программист решает заменить объект функции настоящей функцией, которая к тому же оформлена как подставляемая (inline):

inline

bool doubleGreater(double dl. double d2) {

return dl>d2;

sort(V.begi n(),V.end(),doubleGreater):

Как ни странно, хронометраж двух вызовов sort показывает, что вызов с greater-<double> почти всегда работает быстрее. В своих тестах я сортировал вектор, содержащий миллион чисел типа double, на четырех разных платформах STL с оптимизацией по скорости, и версия с greater<double> всегда работала быстрее. В худшем случае выигрыш в скорости составил 50%, в лучшем он достигал 160%. Вот тебе и плата за абстракцию ...



Факт объясняется просто. Если функция operatorO объекта функции была объявлена подставляемой (явно, с ключевым словом i nl i ne, или косвенно, посредством определения внутри определения класса), большинство компиляторов благополучно подставляет эту функцию во время создания экземпляра шаблона при вызове алгоритма. В приведенном выше примере это происходит с функцией greater<double>:: operator О. В результате код sort не содержит ни одного вызова функций, а для такого кода компилятор может выполнить оптимизацию, недоступную при наличии вызовов (связь между подстановкой функций и оптимизацией компиляторов рассматривается в совете 33 Effective С+-1- и главах 8-10 книги Efficient C++t> [10]).

При вызове sort с передачей doubl eGreater ситуация выглядит иначе. Чтобы убедиться в этом, необходимо вспомнить, что передача функции в качестве параметра другой функции невозможна. При попытке передачи функции в качестве параметра компилятор автоматически преобразует функцию в указатель на эту функцию, поэтому при вызове передается указатель. Таким образом, при вызове

sort(V.begi п().V.end().doubleGreater);

алгоритму sort передается не doubl eGreater, a указатель на doubl eGreater. При создании экземпляра шаблона объявление сгенерированной функции выглядит так:

void sort(vector<double>::iterator first, Начало интервала vector<double>:iterator last, Конец интервала bool (*conip)(double,double)); Функция сравнения

Поскольку comp является указателем на функцию, при каждом его использовании внутри sort происходит косвенный вызов функции (то есть вызов через указатель). Большинство компиляторов не пытается подставлять вызовы функций, вызываемых через указатели, даже если функция объявлена с ключевым словом inl ine и оптимизация выглядит очевидной. Почему? Наверное, потому, что разработчики компиляторов не считают нужным ее реализовать. Пожалейте их - народ постоянно чего-нибудь требует, а успеть все невозможно. Впрочем, это вовсе не означает, что требовать не нужно.

Подавление подстановки кода функций объясняет один факт, который кажется невероятным многим опытным программистам С: функция С++ sort почти всегда превосходит по скорости функцию С qsort. Конечно, в С++ приходится создавать экземпляры шаблонов функций и вызывать operatorO, тогда как в С все ограничивается простым вызовом функции, однако все излишества С++ теряются во время компиляции. На стадии выполнения sort обращается к подставленной функции сравнения (при условии, что функция была объявлена с ключевым словом inl ine, а ее тело доступно на стадии компиляции), тогда как qsort вызывает функцию сравнения через указатель. Результат - sort работает гораздо быстрее. В моих тестах с вектором, содержащим миллион чисел double, превосходство по скорости достигало 670%, но я не призываю верить мне на слово. Вы легко убедитесь в том, что при передаче объектов функций в качестве параметров алгоритмов плата за абстракцию превращается в премию за абстракцию .

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



1 ... 54 55 56 [ 57 ] 58 59 60 ... 71

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