Чтение онлайн

на главную - закладки

Жанры

Эффективное использование STL
Шрифт:

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

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

<x, y>
. В цикле это делается так:

vector<int> v;

int х, у;

vector<int>::iterator i=v.begin; // Перебирать элементы, начиная

for(; i!=v.end; ++i){ // с v.begin, до нахождения нужного

 if(*i > x && *i < y)) break; // элемента или достижения v.end

}

… // После завершения цикла

// i указывает на искомый элемент

// или совпадает с v.end

То же самое можно сделать и при помощи

find_if
, но для этого придется воспользоваться нестандартным адаптером объекта функции — например,
compose2
из реализации SGI (см. совет 50):

vector<int>::iterator i =

 find_if(v.begin, v.end, // Найти первое значение val,

 compose2(logical_and<bool>, // для которого одновременно

 bind2nd(greater<int>, x), // истинны условия

 bind2nd(less<int>, y))); // val>x, и val<y

Но даже если бы нестандартные компоненты не использовались, многие программисты полагают, что вызов алгоритма значительно уступает циклу по наглядности, и я склонен с ними согласиться (см. совет 47).

Вызов

find_if
можно было бы упростить за счет выделения логики проверки в отдельный класс функтора.

template<typename T>

class BetweenValues:

 public unary_function<T, bool> { // См. совет 40

public:

 BetweenValues(const T& lowValue, const T& highValue) :

lowVal(lowValue), highVal(highValue) {}

 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
повышает уровень абстракции и упрощает программирование, документирование, усовершенствование и сопровождение программы.

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

Громовая поступь. Трилогия

Мазуров Дмитрий
Громовая поступь
Фантастика:
фэнтези
рпг
4.50
рейтинг книги
Громовая поступь. Трилогия

Гримуар темного лорда VII

Грехов Тимофей
7. Гримуар темного лорда
Фантастика:
боевая фантастика
попаданцы
аниме
фэнтези
5.25
рейтинг книги
Гримуар темного лорда VII

Товарищ "Чума" 3

lanpirot
3. Товарищ "Чума"
Фантастика:
городское фэнтези
попаданцы
альтернативная история
5.00
рейтинг книги
Товарищ Чума 3

Ненаглядная жена его светлости

Зика Натаэль
Любовные романы:
любовно-фантастические романы
6.23
рейтинг книги
Ненаглядная жена его светлости

Инженер Петра Великого 5

Гросов Виктор
5. Инженер Петра Великого
Фантастика:
попаданцы
альтернативная история
фэнтези
4.75
рейтинг книги
Инженер Петра Великого 5

Хозяин Стужи 8

Петров Максим Николаевич
8. Злой Лед
Фантастика:
аниме
фэнтези
попаданцы
5.00
рейтинг книги
Хозяин Стужи 8

Рассвет русского царства

Грехов Тимофей
1. Новая Русь
Документальная литература:
историческая литература
5.00
рейтинг книги
Рассвет русского царства

Маска Цирцеи (сборник)

Каттнер Генри
Фантастика:
научная фантастика
6.25
рейтинг книги
Маска Цирцеи (сборник)

Чайлдфри

Тоцка Тала
Любовные романы:
современные любовные романы
6.51
рейтинг книги
Чайлдфри

Черный маг императора 3

Герда Александр
3. Черный маг императора
Фантастика:
попаданцы
аниме
5.00
рейтинг книги
Черный маг императора 3

Кодекс Охотника. Книга XXII

Винокуров Юрий
22. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
5.00
рейтинг книги
Кодекс Охотника. Книга XXII

Газлайтер. Том 23

Володин Григорий Григорьевич
23. История Телепата
Фантастика:
боевая фантастика
попаданцы
аниме
5.00
рейтинг книги
Газлайтер. Том 23

Личный аптекарь императора. Том 2

Карелин Сергей Витальевич
2. Личный аптекарь императора
Фантастика:
городское фэнтези
попаданцы
аниме
5.00
рейтинг книги
Личный аптекарь императора. Том 2

Личинка

Привалов Сергей
1. Звездный Бродяга
Фантастика:
боевая фантастика
космическая фантастика
рпг
попаданцы
5.00
рейтинг книги
Личинка