Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL_2.doc
Скачиваний:
0
Добавлен:
01.05.2025
Размер:
512.51 Кб
Скачать

2.5. Адаптеры

Компонент, который изменяет интерфейс другого компонента, называется адаптером. Адаптеры изображены в последнем столбце на рис. 2.1. Например, reverse_iterator представляет собой компонент, который адаптирует тип итератора в новый тип итератора с теми же возможностями, что и у исходного, но с обратным направлением прохода. Это оказывается полезным, так как для ряда задач требуется именно такой проход. Например, алгоритм find возвращает итератор, указывающий на первое найденное в последовательности значение, но может оказаться, что нас интересует последнее значение. Мы можем обратить

порядок элементов в последовательности, применяя алгоритм reverse, а затем применить алгоритм find, но мы можем добиться того же, не изменяя и не копируя исходную последовательность, воспользовавшись адаптером reverse_iterator. На рис. 2.1 этот адаптер изображен как шлейф, в котором перекрещиваются проводники для ++ и --.

Продолжая использовать в качестве примера алгоритм accumulate, можно не увидеть никакой пользы в обращении порядка накапливаемых значений, поскольку сумма не должна изменяться при изменении порядка суммирования. Это справедливо для последовательности целых чисел и + в качестве функции накопления, поскольку для целочисленного сложения выполняются закон ассоциативности (x + y) + z = x + (y + z) и закон коммутативности

х + y = У + х . Однако эти законы не выполняются при сложении чисел с плавающей точкой, что связано с ошибками округления и переполнения (из-за переполнения ассоциативность может нарушиться даже для чисел типа int). В случае чисел с плавающей точкой ошибки округления обычно оказываются меньшими, если числа суммируются в порядке возрастания; в противном

случае значения, оказывающиеся слишком малыми по сравнению с текущей суммой, могут вообще никак на нее не повлиять. Предположим, что у нас имеется вектор значений в порядке убывания, и мы хотим вычислить их сумму. Чтобы выполнить сложение в порядке возрастания, мы можем воспользоваться алгоритмом accumulate с обратными итераторами.

Пример 2.15. Демонстрация обобщенного алгоритма accumulate с обратным итератором

#include <iostream>

#include <vector>

#include <cassert>

#include <numeric> // Алгоритм accumulate

using namespace std;

int main()

{

cout << "Демонстрация обобщенного алгоритма\п"

<< "accumulate с обратным итератором."

< < endl;

float small = (float)1.О/(1 << 27);

float x[5] = {1.O, 3*small/ 2*small, small, small};

// Инициализация вектора элементами от х[0] до х[4]:

vector<float> vector1(&x[0], &х[5]);

cout << "Суммируемые значения: " << endl;

vector<float>::iterator i;

cout.precision(10);

for (i = vector1.begin(); i != vector1.end(); ++i)

cout << *i << endl;

cout << endl;

float sum = accumulate(vector1.begin(),

vector1.end(),

(float)0.0);

cout << "Сумма слева направо = " << sum << endl;

float sum1 = accumulate(vector1.rbegin(),

vector1.rend(),

(float)0.0);

cout << "Сумма справа налево = "

<< (double)sum1 << endl;

return 0;

}

Для вычисления sum1 для получения итераторов типа

vector<float>::reverse_iterator мы используем функции-члены vector— rbegin и rend. Итератор vector<float>::reverse_iterator, подобно итератору

vector<float>::iterator, определен как часть интерфейса vector. Вывод представленной программы зависит от точности типа float, но значение small выбрано достаточно малым, чтобы разница между sum и sum1 обнаружилась при точности около десяти десятичных знаков.6 Вывод программы при использовании компилятора VC++ имеет следующий вид.

Вывод примера 2.15

Демонстрация обобщенного алгоритма

accumulate с обратным итератором.

Суммируемые значения:

1

2.235174179е-008

1.490116119е-008

7.450580597е-009

7.450580597е-009

Сумма слева направо = 1

Сумма справа налево = 1.000000052

Сумма, накопленная при суммировании справа налево, начиная с малых значений,

оказывается более точной.

Тип vector<float>::reverse_iterator в действительности определен с

использованием адаптера итератора. Мы можем использовать этот адаптер непосредственно в нашей программе, записав

reverse_iterator<vector<float>::iterator>

start(vector1.end()), finish(vector1.begin());

float sum1 = accumulate(start, finish, (float)0.0);

Здесь start и finish объявлены как переменные типа

reverse_iterator<vector<float>::iterator>

в котором параметр шаблона vector<float>::iterator представляет собой тип

итератора. Этот тип reverse_iterator предоставляет операторы ++ и --, так же как и тип vector<float>::iterator, но их смысл меняется на обратный. Для удобства каждый из типов контейнеров STL предоставляет определенный таким образом тип reverse_iterator, а также функции-члены rbegin и rend, которые возвращают итераторы указанного типа.

STL определяет также несколько видов адаптеров контейнеров и функций. Адаптер стека преобразует последовательный контейнер в контейнер с ограниченным интерфейсом стека "последний вошел — первый вышел". Адаптер очереди преобразует последовательный контейнер в контейнер с ограниченным интерфейсом очереди "первый вошел — первый вышел", а адаптер очереди с приоритетами создает очередь, в которой значения доступны в порядке, управляемом сравниваемым параметром. Эти адаптеры будут более полно описаны в главе 9, "Адаптеры контейнеров".

Функциональные адаптеры, предоставляемые STL, включают инверторы, связыватели и адаптеры для указателей на функции. Инвертор (negator) представляет собой функциональный адаптер, используемый для обращения смысла предиката, который представляет собой функциональный объект, возвращающий значение типа bool. Связыватель (binder) применяется для преобразования бинарного функционального объекта в унарный путем связывания

аргумента с некоторым определенным значением. Адаптер для указателя на функцию преобразует указатель на функцию в функциональный объект; он может использоваться для придания компилируемому коду большей гибкости, чем при применении стандартных функциональных объектов (что помогает избежать "разбухания кода", являющегося результатом слишком большого количества компилируемых комбинаций алгоритмов и функциональных объектов в одной программе). Подробно эти функциональные адаптеры рассматриваются в главе 11, "Функциональные адаптеры".

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]