Вход/Регистрация
Программирование. Принципы и практика использования C++ Исправленное издание
вернуться

Страуструп Бьерн

Шрифт:

Объекты-функции, как правило, очень эффективны. В частности, передача по значению небольшого объекта-функции в качестве аргумента шаблонной функции обеспечивает оптимальную производительность. Причина проста, но удивительна для людей, хорошо знающих механизм передачи функций в качестве аргументов: обычно передача функции в виде объекта приводит к созданию значительно более маленького и быстродействующего кода, чем при передаче функции как таковой! Это утверждение оказывается истинным, только если объект мал (например, если он содержит одно-два слова данных или вообще не хранит данные) или передается по ссылке, а также если оператор вызова функции невелик (например, простое сравнение с помощью оператора
<
) и определен как подставляемая функция (например, если его определение содержится в теле класса). Большинство примеров в этой главе — и в книге в целом — соответствует этому правилу. Основная причина высокой производительности небольших и простых объектов-функций состоит в том, что они предоставляют компилятору объем информации о типе, достаточный для того, чтобы сгенерировать оптимальный код. Даже устаревшие компиляторы с несложными оптимизаторами могут генерировать простую машинную инструкцию “больше” для сравнения в классе
Larger_than
, вместо вызова функции. Вызов функции обычно выполняется в 10–50 раз дольше, чем простая операция сравнения. Кроме того, код для вызова функции больше, чем код простого сравнения.

21.4.2. Предикаты на членах класса

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

int
и
double
. Однако в некоторых предметных областях более широко используются контейнеры объектов пользовательских классов. Рассмотрим пример, играющий главную роль во многих областях, — сортировка записей по нескольким критериям.

struct Record {

string name; // стандартная строка

char addr[24]; // старый стиль для согласованности

// с базами данных

// ...

};

vector<Record> vr;

Иногда мы хотим сортировать вектор

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

// ...

sort(vr.begin,vr.end,Cmp_by_name); // сортировка по имени

// ...

sort(vr.begin,vr.end,Cmp_by_addr); // сортировка по адресу

// ...

Cmp_by_name
— это объект-функция, сравнивающий два объекта класса
Record
по членам
name
. Для того чтобы дать пользователю возможность задавать критерий сравнения, в стандартном алгоритме
sort
предусмотрен необязательный третий аргумент, указывающий критерий сортировки. Функция
Cmp_by_name
создает объект
Cmp_by_name
для алгоритма
sort
, чтобы использовать его для сравнения объектов класса
Record
. Это выглядит отлично, в том смысле, что нам не приходится об этом беспокоиться самим. Все, что мы должны сделать, — определить классы
Cmp_by_name
и
Cmp_by_addr
.

// разные сравнения объектов класса Record:

struct Cmp_by_name {

bool operator(const Record& a,const Record& b) const

{ return a.name < b.name; }

};

struct Cmp_by_addr {

bool operator(const Record& a, const Record& b) const

{ return strncmp(a.addr,b.addr,24) < 0; } // !!!

};

Класс

Cmp_by_name
совершенно очевиден. Оператор вызова функции
operator
просто сравнивает строки
name
, используя оператор
<
из стандартного класса
string
. Однако сравнение в классе
Cmp_by_addr
выглядит ужасно. Это объясняется тем, что мы выбрали неудачное представление адреса — в виде массива, состоящего из 24 символов (и не завершающегося нулем). Мы сделали этот выбор частично для того, чтобы показать, как объект-функцию можно использовать для сокрытия некрасивого и уязвимого для ошибок кода, а частично для того, чтобы продемонстрировать, что библиотека STL может решать даже ужасные, но важные с практической точки зрения задачи. Функция сравнения использует стандартную функцию
strncmp
, которая сравнивает массивы символов фиксированной длины и возвращает отрицательное число, если вторая строка лексикографически больше, чем первая. Как только вам потребуется выполнить такое устаревшее сравнение, вспомните об этой функции (см., например, раздел Б.10.3).

21.5. Численные алгоритмы

Большинство стандартных алгоритмов из библиотеки STL связаны с обработкой данных: они их копируют, сортируют, выполняют поиск среди них и т.д. В то же время некоторые из них предназначены для вычислений. Они могут оказаться полезными как для решения конкретных задач, так и для демонстрации общих принципов реализации численных алгоритмов в библиотеке STL. Существуют всего четыре таких алгоритма.

Эти алгоритмы определены в заголовке

<numeric>
. Мы опишем первые два из них, а остальные при необходимости читатели могут изучить самостоятельно.

21.5.1. Алгоритм accumulate

Простейшим и наиболее полезным численным алгоритмом является алгоритм

accumulate
. В простейшем варианте он суммирует значения, принадлежащие последовательности.

template<class In, class T> T accumulate(In first, In last, T init)

  • Читать дальше
  • 1
  • ...
  • 327
  • 328
  • 329
  • 330
  • 331
  • 332
  • 333
  • 334
  • 335
  • 336
  • 337
  • ...

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

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

  • Моя полка

Контакты

  • chitat.ebooker@gmail.com

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