Вход/Регистрация
Эффективное использование STL
вернуться

Мейерс Скотт

Шрифт:

 bool operator(const T& val) const {

return val > lowVal && val < highVal;

 }

private:

 T lowVal;

 T highVal;

};

…

vector<int> iterator i = find_if(v.begin, v.end,

 BetweenValues<int>(x, y));

Однако у такого решения имеются свои недостатки. Во-первых, создание шаблона

BetweenValues
требует значительно большей работы, чем простое написание тела цикла. Достаточно посчитать строки в программе: тело цикла — одна строка,
BetweenValues
— четырнадцать строк. Соотношение явно не в пользу алгоритма. Во-вторых, описание критерия поиска физически отделяется от вызова. Чтобы понять смысл вызова
find_if
, необходимо найти определение
BetweenValues
, но оно должно располагаться вне функции, содержащей вызов
find_if
. Попытка объявить
BetweenValues
внутри функции, содержащей вызов
find_if
:

{ // Начало функции

 …

 template<typename T>

 class BetweenValues: public unary_function<T, bool> {…};

 vector<int>::iterator i = find_if(v.begin, v.end,

BetweenValues<int>(x, у));

} // Конец функции

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

BetweenValues
в виде класса:

{ // Начало функции

 …

 class BetweenValues: public unary_function<int, bool> {…};

 vector<int>::iterator i = find_if(v.begin, v.end,

BetweenValues(x, y));

} // Конец функции

все равно ничего не получается, поскольку классы, определяемые внутри функций, являются локальными, а локальные классы не могут передаваться в качестве аргументов шаблонов (как функтор, передаваемый

find_if
). Печально, но классы функторов и шаблоны классов функторов не разрешается определять внутри функций, как бы удобно это ни было.

В контексте борьбы между вызовами алгоритмов и циклами это означает, что выбор определяется исключительно содержимым цикла. Если алгоритм уже умеет делать то, что требуется, или нечто очень близкое, вызов алгоритма более нагляден. Если задача элементарно решается в цикле, а при использовании алгоритма требует сложных нагромождений адаптеров или определения отдельного класса функтора, вероятно, лучше ограничиться циклом. Наконец, если в цикле приходится выполнять очень длинные и сложные операции, выбор снова склоняется в пользу алгоритмов, потому что длинные и сложные операции лучше оформлять в отдельных функциях. После того как тело цикла будет перенесено в отдельную функцию, почти всегда удается передать эту функцию алгоритму (особенно часто — алгоритму

for_each
) так, чтобы полученный код был более наглядным и прямолинейным.

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

for
,
while
и
do
высокоуровневыми терминами
insert
,
find
и
foreach
повышает уровень абстракции и упрощает программирование, документирование, усовершенствование и сопровождение программы.

Совет 44. Используйте функции контейнеров вместо одноименных алгоритмов

Некоторые контейнеры содержат функции, имена которых совпадают с именами алгоритмов STL. Так, в ассоциативных контейнерах существуют функции

count
,
find
,
lower_bound
,
upper_bound
и
equal_range
, а в контейнере
list
предусмотрены функции
remove
,
remove_if
,
unique
,
sort
,
merge
и
reverse
. Как правило, эти функции используются вместо одноименных алгоритмов, что объясняется двумя причинами. Во-первых, функции классов быстрее работают. Во-вторых, они лучше интегрированы с контейнерами (особенно ассоциативными), чем алгоритмы. Дело в том, что алгоритмы и одноименные функции классов обычно работают не совсем одинаково.

Начнем с ассоциативных контейнеров. Допустим, имеется множество

set<int>
, содержащее миллион значений, и вы хотите найти позицию первого вхождения числа 727, если оно присутствует. Ниже приведены два очевидных способа поиска:

set<int> s; // Создать множество

… // и занести в него

// миллион чисел

set<int>::iterator i = s.find(727); // Функция find контейнера

  • Читать дальше
  • 1
  • ...
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • ...

Ебукер (ebooker) – онлайн-библиотека на русском языке. Книги доступны онлайн, без утомительной регистрации. Огромный выбор и удобный дизайн, позволяющий читать без проблем. Добавляйте сайт в закладки! Все произведения загружаются пользователями: если считаете, что ваши авторские права нарушены – используйте форму обратной связи.

Полезные ссылки

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

Подпишитесь на рассылку: