Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
C++ для начинающих (Стенли Липпман) 3-е хххх.pdf
Скачиваний:
84
Добавлен:
30.05.2015
Размер:
5.92 Mб
Скачать

С++ для начинающих

1078

left

 

Добавлять символ заполнения справа от значения

 

right

 

Добавлять символ заполнения слева от значения

internal

 

Добавлять символ заполнения между знаком и значением

*fixed

 

Отображать число с плавающей точкой в десятичной нотации

scientific

 

Отображать число с плавающей точкой в научной нотации

flush

 

Сбросить буфер ostream

ends

 

Вставить нулевой символ, затем сбросить буфер ostream

endl

 

Вставить символ новой строки, затем сбросить буфер ostream

ws

 

Пропускать пробельные символы

 

 

 

// для этих манипуляторов требуется #include <ionamip>

setfill( ch)

 

Заполнять пустое место символом ch

Setprecision( n )

 

Установить точность вывода числа с плавающей точкой равной n

setw( w )

 

Установить ширину поля ввода или вывода равной w

setbase( b )

 

Выводить целые числа по основанию b

* обозначает состояние потока по умолчанию

20.10. Сильно типизированная библиотека

Библиотека iostream сильно типизирована. Например, попытка прочитать из объекта

класса ostream или записать в объект класса istream помечается компилятором как

#include <iostream> #include <fstream> class Screen;

extern istream& operator>>( istream&, const Screen& ); extern void print( ostream& );

нарушение типизации. Так, если имеется набор объявлений: ifstream inFile;

то следующие две инструкции приводят к нарушению типизации, обнаруживаемому во время компиляции:

С++ для начинающих

1079

int main()

{

Screen myScreen;

//ошибка: ожидается ostream& print( cin >> myScreen );

//ошибка: ожидается оператор >> inFile << "ошибка: оператор вывода";

Средства ввода/вывода включены в состав стандартной библиотеки C++. В главе 20 библиотека iostream описана не полностью, в частности вопрос о создании

определенных пользователем манипуляторов и буферных классов остался за рамками введения в язык. Мы сосредоточили внимание лишь на той части библиотеки iostream, которая имеет основополагающее значение для программного ввода/вывода.

С++ для начинающих

1080

Приложение

21. Обобщенные алгоритмы в

алфавитном порядке

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

Первыми двумя аргументами всех обобщенных алгоритмов (естественно, не без исключений) является пара итераторов, обычно first и last, обозначающих диапазон элементов внутри контейнера или встроенного массива, над которым работает алгоритм. Этот диапазон (часто называемый интервалом с включенной левой

//следует читать: включая first и все последующие

//элементы до last, но не включая сам last

границей), как правило, записывается в виде:

[ first, last )

Это означает, что диапазон начинается с first и заканчивается last, однако сам элемент last не включается. Если

first == last

то говорят, что диапазон пуст.

К паре итераторов предъявляется такое требование: last должен быть достижим, если начать с first и последовательно применять оператор инкремента. Однако компилятор не может проверить выполнение данного ограничения. Если требование не будет выполнено, поведение программы не определено; обычно это заканчивается ее крахом и дампом памяти.

В объявлении каждого алгоритма подразумевается минимальная поддержка, которую должны обеспечить итераторы (краткое обсуждение пяти категорий итераторов см. в разделе 12.4). Например, алгоритм find(), реализующий однопроходный обход контейнера и выполняющий только чтение, требует итератора чтения InputIterator. Ему также можно передать одно- или двунаправленный итератор или итератор с произвольным доступом. Однако передача итератора записи приведет к ошибке. Не гарантируется, что подобные ошибки (при передаче итератора неподходящей категории) будут обнаружены компилятором, поскольку категории итераторов это не сами типы, а лишь параметры, которыми конкретизируется шаблон функции.

Некоторые алгоритмы существуют в нескольких вариантах: в одном используется тот или иной встроенный оператор, а в другом объект-функция или указатель на функцию, реализующие альтернативу этому оператору. Например, алгоритм unique()

по умолчанию сравнивает соседние элементы контейнера с помощью оператора

С++ для начинающих

1081

равенства, определенного в классе, к которому данные элементы принадлежат. Но если

в этом классе нет оператора равенства или мы хотим сравнивать элементы иным способом, то можем передать объект-функцию или указатель на функцию, поддерживающую нужную семантику. Есть и такие алгоритмы, которые выполняют похожие действия, но имеют различные имена. Так, имена предикатных версий алгоритмов всегда заканчиваются на _if, скажем find_if(). Например, есть вариант алгоритма replace(), где используется встроенный оператор равенства, и вариант с именем replace_if(), которому передается предикатный объект-функция или указатель на функцию-предикат.

Алгоритмы, модифицирующие контейнер, обычно также существуют в двух вариантах: один осуществляет модификацию по месту, а второй возвращает копию с внесенными изменениями. Так, есть алгоритмы replace() и replace_copy(). Однако вариант с копированием (его имя всегда содержит слово _copy) имеется не для каждого алгоритма, модифицирующего контейнер. К примеру, для алгоритмов sort() его нет. В таких случаях, если нужно, чтобы алгоритм работал с копией, мы должны создать ее самостоятельно и передать в качестве аргумента.

Для использования любого обобщенного алгоритма в программу необходимо включить заголовочный файл

#include <algorithm>

Для

употребления

любого

из

четырех

численных

алгоритмов:

adjacent_difference(), accumulate(),

inner_product() и partial_sum()

нужно включить также файл

 

 

 

 

#include <numeric>

Приведенные в этом Приложении примеры программ, в которых используются алгоритмы и различные контейнерные типы, отражают существующую на момент написания книги реализацию. Применение библиотеки ввода/вывода iostream следует соглашениям, установленным до принятия стандарта; скажем, в программу включается заголовочный файл iostream.h, а не iostream. Шаблоны не поддерживают аргументы по умолчанию. Чтобы программа работала на системе, имеющейся у вас, возможно, придется изменить некоторые объявления.

Другое, более подробное, чем в этой книге, описание обобщенных алгоритмов можно найти в работе [MUSSER96], правда, оно несколько отстает от окончательного варианта стандартной библиотеки C++.

Алгоритм accumulate()

С++ для начинающих

1082

template < class InputIterator, class Type > Type accumulate(

InputIterator first, InputIterator last, Type init );

template < class InputIterator, class Type, class BinaryOperation >

Type accumulate(

InputIterator first, InputIterator last,

Type init, BinaryOperation op );

Первый вариант accumulate() вычисляет сумму значений элементов последовательности из диапазона, ограниченного парой итераторов [first,last), с начальным значением, которое задано параметром init. Например, если дана последовательность {1,1,2,3,5,8} и начальное значение 0, то результатом работы алгоритма будет 20. Во втором варианте вместо оператора сложения к элементам применяется переданная бинарная операция. Если бы мы передали алгоритму accumulate() объект-функцию times<int> и начальное значение 1, то получили бы результат 240. accumulate() это один из численных алгоритмов; для его

#include <numeric> #include <list> #include <functional> #include <iostream.h>

/*

*выход:

*accumulate()

*работает с последовательностью {1,2,3,4}

*результат для сложения по умолчанию: 10

*результат для объекта-функции plus<int>: 10

*/

int main()

{

int ia[] = { 1, 2, 3, 4 }; list<int,allocator> ilist( ia, ia+4 );

int ia_result = accumulate(&ia[0], &ia[4], 0); int ilist_res = accumulate(

ilist.begin(), ilist.end(), 0, plus<int>() );

cout << "accumulate()\n\t"

<<"работает с последовательностью {1,2,3,4}\n\t"

<<"результат для сложения по умолчанию: "

<<ia_result << "\n\t"

<<"результат для объекта-функции plus<int>: "

<<ilist_res

<<endl;

return 0;

использования в программу необходимо включить заголовочный файл <numeric>.

}