Добавил:
Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:

2vcTnguyvU

.pdf
Скачиваний:
6
Добавлен:
15.04.2023
Размер:
955.27 Кб
Скачать

void print ( const list <double> & lst)

{ // используем итератор для прохода по lst list<double>::const_iterator p;

for (p=lst.begin (); p!=lst.end(); ++p) cout << *p <<'\t';

cout <<endl; }

int main(int argc, char *argv[]) { double w[4]={ 1.7, 0.6, 77, -57};

list<double>

z;

 

for (int i=0; i<4; ++i )

 

z.push_front (w[i]);

print (z);

z.sort ();

print (z);

 

cout <<"Summa = "<<accumulate (z.begin ( ), z.end ( ), 0.0) <<endl; system("PAUSE"); return EXIT_SUCCESS; }

В этом примере списочный контейнер должен хранить переменные с двойной точностью. Массив из таких переменных помещается в список. Функция print ( ) использует итератор для печати всех элементов списка по очереди. Для списочного контейнера используется тип двунаправленного итератора, а это означает, что некоторые обобщенные алгоритмы (в которых используется тип итератора – произвольный доступ) нельзя применить к списку. Поэтому функция sort( ) была реализована в классе контейнера. Функция accumulate ( ) является обобщенной функцией из пакета numeric. Она использует 0.0 в качестве начального значения и вычисляет сумму элементов списочного контейнера путем прохода от начальной позиции z.begin ( ) до конечной z.end ( ).

Ассоциативные контейнеры

Ассоциативные контейнеры – это множества, отображения, мультимножества и мультиотображения. Они содержат доступные по ключу элементы и упорядочивающее отношение Compare, являющееся сравнивающим объектом ассоциативного контейнера. Например6

// ассоциативный контейнер – поиск возраста. файл stl_age.cpp #include <cstdlib>

#include <iostream>

#include <map>

#include <string> using namespace std;

int main(int argc, char *argv[]) {

map <string, int, less<string> > name_age; name_age["Иванов Петя"]=20; name_age["Петрова Юля "] =10;

100

name_age["Сидоров Вовочка"]=7;

cout << "Vovochka "<<name_age["Сидоров Вовочка"]<< " year" << endl; system("PAUSE"); return EXIT_SUCCESS; }

Отображение name_age – это ассоциативный массив, в котором ключом является тип string. Объект Compare – это less<string>.

В следующей таблице, описывающей интерфейс ассоциативных классов, они обозначены как ASSOC.

Определения ассоциативных контейнеров STL

ASSOC::key_type

тип ключа поиска

ASSOC::key_compare

тип сравнивающего объекта

ASSOC::value_compare

тип для сравнения ASSOC::value_type

Инициализация ассоциативных контейнеров обеспечивается несколькими стандартными конструкторами.

Ассоциативные конструкторы STL

ASSOC ( )

конструктор по умолчанию, используется

 

Compare

 

 

ASSOC (cmp )

конструктор, использующий cmp как

 

сравнивающий объект

ASSOC (b_it , e_it )

использует элементы в диапазоне от b_it от

 

e_it применяется Compare

ASSOC (b_it , e_it , cmp )

использует элементы в диапазоне от b_it

 

до e_it и cmp как сравнивающий объект

STL: функции-члены вставки и удаления

c.insert ( t )

 

если ни один из существующих

 

 

элементов не имеет такого же ключа, как

 

 

t, вставляет t; возвращает пару <iterator,

 

 

bool> c

bool, имеющим значение true,

 

 

если t отсутствовал

c.insert (w_it, t )

 

вставляет t

c w_it в качестве начальной

 

 

позиции поиска; терпит неудачу в

 

 

множествах

и отображениях, если

 

 

ключевое значение уже присутствует;

 

 

возвращает позицию вставки

c. insert (b_it , e_it )

 

вставляет диапазон элементов

c. erase (k)

 

стирает

элементы, ключевое значение

 

 

которых равно k, возвращая количество

 

 

стертых элементов

c.erase (w_it)

 

стирает указываемый элемент

c.erase (b_it, e_it )

 

стирает диапазон элементов

 

101

 

 

Вставка выполняется, если ни один из элементов с таким же ключом еще не присутствует. Вот некоторые примеры использования этих членов:

int m [4] = { 2,3,5,7 }; set <int , less<int> > s;

// множество целых, упорядоченное с помощью less set <int , less<int> > t( m, m+4 ); //используется 2, 3, 5, 7 s.insert (3); // помещает 3 в множество s

t. insert (3); // не вставляется, так как в множестве t уже есть 3 s.erase (2); // в s нет такого элемента

t. erase (5); // t теперь содержит 2, 3, 7

Адаптеры контейнеров

Классы адаптеров контейнеров - это контейнерные классы, которые изменяют имеющиеся контейнеры с тем, чтобы обеспечить иное открытое поведение на основе существующей реализации. Три предлагаемых адаптера

контейнеров - это stack (стек) , queue ( очередь )

и priority_queue

(приоритетная очередь).

 

Стек может быть получен (адаптирован) из

вектора, списка и

двусторонней очереди. Он нуждается в реализации, поддерживающей операции push, pop, top.

STL: Функции адаптированного стека

void push (const value_type & v)

помещает v в стек

void pop()

убирает верхний элемент из стека

value_type & top () const

возвращает верхний элемент из стека

bool empty () const

возвращает истину, если стек пуст

size_type size() const

возвращает число элементов в стеке

operator= = и operator <

равенство и лексикографическое меньше

 

чем

Очередь может быть получена из списка или двусторонней очереди. Она нуждается в реализации, поддерживающей операции empty, size, front, back, push_back и pop_front.

STL: Функции адаптированной очереди

void push(const value_type & v)

помещает v в конец очереди

void pop()

удаляет первый элемент из очереди

value_type & front () const

возвращает первый элемент из очереди

bool empty () const

возвращает истину, если очередь пуста

size_type size() const

возвращает число элементов в очереди

operator = = и operator <

равенство и лексикографическое меньше

 

чем

 

102

Приоритетная очередь может быть адаптирована из вектора или двусторонней очереди. Она нуждается в реализации, поддерживающей операции empty, size, front, push_back и pop_back. Кроме того, приоритетной очереди для ее инстанцирования необходим объект сравнения. Как определено отношением сравнения для приоритетной очереди, верхний элемент - это самый большой элемент.

STL: функции приоритетной очереди

void push(const value_type & v)

помещает v в приоритетную очередь

 

void pop()

удаляет верхний элемент приоритетной

 

очереди

 

 

 

value_type & top () const

возвращает верхний элемент

 

 

приоритетной очереди

 

 

bool empty () const

проверяет, не пуста ли приоритетная

 

 

очередь

 

 

 

size_type size() const

показывает

число

элементов

в

 

приоритетной очереди

 

 

Стек может быть получен, в частности, из реализации vector. Обратите внимание, как ATD из STL заменяет самостоятельно разработанные реализации этих типов.

Пример адаптированного стека из реализации vector. #include <cstdlib>

#include <iostream>

#include <stack>

#include <vector>

#include <string> using namespace std;

int main(int argc, char *argv[]) { stack<string, vector<string> > str_stack;

string poems[3]= {" East-from upper Canada to the deep South\n", " -is

magnificent, and far outshines anything \n", " the Old World has to offer.\n"}; for (int i=0; i<3; ++i)

str_stack.push(poems[i]); while (!str_stack.empty() )

{

cout<<str_stack.top(); str_stack.pop (); }

system("PAUSE"); return EXIT_SUCCESS; }

103

Итераторы

Существует пять типов итераторов: ввода, вывода, прохода вперед, двусторонние и произвольного доступа. Не все типы итераторов могут быть доступны для данного контейнерного класса. Например, итератор произвольного доступа доступен для векторов, но не для отображений.

Итераторы ввода (input iterators ) поддерживают операции равенства, разыменования и автоинкремента. Итераторы, отвечающие этим условиям, могут использоваться для однопроходных алгоритмов, которые читают значения структуры данных в одном направлении. Специальным случаем итератора ввода является istream_iterator.

Итераторы вывода (output iterators ) поддерживает разыменование, допустимое только с левой стороны присваивания, и автоинкремент. Итераторы, отвечающие этим условиям, могут использоваться для однопроходных алгоритмов, которые пишут значения в структуры данных в одном направлении. Специальным случаем итератора вывода является ostream_iterator.

Итераторы прохода вперед (forward iterators ) или однонаправленные итераторы поддерживают все операции итераторов ввода/вывода и, кроме того, позволяют без ограничений применять присваивание. Благодаря этому можно сохранять позицию внутри структуры данных при многократных проходах. Таким образом, с помощью итератора прохода вперед можно написать общий однонаправленный многопроходный алгоритм.

Двусторонние итераторы (bidirectional iterators ) поддерживают все операции итераторов прохода вперед, а также автоинкремент и автодекремент. Таким образом, с помощью двустороннего итератора можно написать общий двунаправленный многопроходной алгоритм.

Итераторы произвольного доступа (random acces iterators) поддерживает все операции двусторонних итераторов, а также арифметические адресные операции, такие как индексирование. Кроме того, итераторы произвольного доступа поддерживают операции сравнения. Таким образом, с помощью итераторов произвольного доступа можно написать такие алгоритмы, как quicksort (быстрая сортировка), которые требуют эффективного произвольного доступа.

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

104

Итераторы istream_iterator и ostream_iterator

Итератор istream_iterator происходит от итератора ввода и работает исключительно с чтением из потоков. Итератор ostream_iterator происходит от итератора вывода и работает исключительно с записью в потоки.

Пример программы использования istream_iterator и ostream_iterator. В программе создается вектор, состоящий из пяти элементов. Затем в эти элементы вводятся значения, вычисляется их сумма и сумма отображается на экране. Шаблон для istream_iterator инстанцируется как <тип, расстояние > . Это расстояние обычно задается как ptrdiff_t, это целый тип, представляющий разницу между двумя значениями указателей.

#include <cstdlib> #include <iostream>

#include <iterator>

#include <vector> using namespace std;

int main(int argc, char *argv[]) { vector<int> d(5); int sum; cout<<"Enter first"<<endl; //istream_iterator<int, ptrdiff_t> in(cin); istream_iterator<int> in(cin); ostream_iterator<int> out(cout, "\t"); sum=d[0]=*in; //enter first number

cout << "Enter 4 number " << endl; for ( int i=1; i<5; ++i)//enter other {

d[i]=*++in;

sum+=d[i]; }

for (int i=0; i<5; ++i )

*out=d[i];

cout << "Summa = "<<sum << endl;

system("PAUSE"); return EXIT_SUCCESS; }

Адаптеры итераторов

Итераторы могут быть адаптированы для обеспечения обратного просмотра и просмотра со вставкой

Обратные итераторы (Reverse iterators)

Двунаправленные итераторы и итераторы произвольного доступа имеют соответствующие адаптеры обратных итераторов, которые выполняют итерации через структуру данных в противоположном направлении.Они имеют те же самые сигнатуры, как и соответствующие итераторы. Фундаментальное соотношение между обратным итератором и его соответствующим итератором i установлено тождеством

&*(reverse_iterator(i)) == &*(i - 1).

105

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

Итераторы вставки (Insert iterators)

Чтобы было возможно иметь дело с вставкой таким же образом, как с записью в массив, в библиотеке обеспечивается специальный вид адаптеров итераторов, называемых итераторами вставки (insert iterators). С обычными классами итераторов: while (first != last) *result++ = *first++;

вызывает копирование диапазона [first, last) в диапазон, начинающийся с result. Тот же самый код с result, являющимся итератором вставки, вставит соответствующие элементы в контейнер. Такой механизм позволяет всем алгоритмам копирования в библиотеке работать в режиме вставки (insert mode) вместо обычного режима наложения записей.

В следующем примере для прохода по последовательности использован обратный итератор.

#include <cstdlib> #include <iostream>

# include <vector> using namespace std;

template <class ForwIter>

void print(ForwIter first, ForwIter last, const char * str) { cout<<str<<endl;

while (first!=last) cout<<(*first++) <<'\t'; cout<<endl; }

int main(int argc, char *argv[]) {

int data[4]={ 5,2,7,11}; vector<int> d(data, data+4); vector<int>::reverse_iterator rp=d.rbegin();

print(d.begin(), d.end(), "Initial"); print (rp, d.rend(), "Turned"); system("PAUSE"); return EXIT_SUCCESS; }

106

Алгоритмы

Библиотека алгоритмов STL содержит четыре категории алгоритмов:

алгоритмы сортировки;

не изменяющие последовательность алгоритмы;

изменяющие последовательность алгоритмы;

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

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

Алгоритмы сортировки

Алгоритмы сортировки включают общую сортировку, слияние, лексикографическое сравнение, перестановку, двоичный поиск и некоторые сходные операции. Эти алгоритмы имеют версии, использующие либо operator<(), либо объект Compare. Часто они нуждаются в итераторе произвольного доступа. Например, библиотечный прототип для алгоритма сортировки:

template <class RanAcc>

void sort (RandAcc b, RandAcc d);

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

#include <cstdlib> #include <iostream>

# include <iterator> using namespace std;

int main(int argc, char *argv[]) {

int array[] = { 1, 50, -10, 11, 42, 19 };

int count = sizeof (array) / sizeof (array[0]); ostream_iterator <int> iter (cout, " ");

cout << "before: ";

copy (array, array + count, iter);

cout << "\nafter: ";

sort (array, array + count, greater<int>());

copy (array, array + count, iter); cout << endl;

system("PAUSE");

return EXIT_SUCCESS; }

107

Не изменяющие последовательность алгоритмы

Неизменяющие алгоритмы не модифицируют содержимое контейнеров, с которыми они работают. Типичная подобная операция – поиск в контейнере конкретного элемента и возвращение его позиции.

template <class InputIterator, class T>

InputIterator find(InputIterator first, InputIterator last, const T& value); template <class InputIterator, class Predicate>

InputIterator find_if(InputIterator first, InputIterator last, Predicate pred);

find возвращает первый итератор i в диапазоне [first, last), для которого соблюдаются следующие соответствующие условия: *i == value, pred (*i) == true. Если такой итератор не найден, возвращается last. Соответствующий предикат применяется точно find(first, last, value) - first раз.

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

# include <vector>

 

# include <algorithm>

 

using namespace std;

 

bool div_3 (int a_)

 

{ return a_ % 3 ? 0 : 1; }

 

int main(int argc, char *argv[])

{

typedef vector <int> IntVec;

 

IntVec v (10);

 

for (int i = 0; i < v.size (); i++)

v[i] = (i + 1) * (i + 1);

IntVec::iterator iter;

iter = find_if (v.begin (), v.end (), div_3); if (iter != v.end ())

cout << "Value " << *iter << " at offset " << (iter - v.begin ()) << " is divisible by 3" << endl;

108

system("PAUSE");

return EXIT_SUCCESS; }

Изменяющие последовательность алгоритмы

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

swap(t1, t2)

 

меняет местами t1 и t2

 

 

iter_swap (b1 , b2)

меняет местами элементы, на которые

 

 

направлены указатели

 

 

transform (b1, e1, b2, op )

с помощью унарного оператора op

 

 

трансформирует последовательность от b1

 

 

до e1, помещает ее в b2, возвращает

 

 

положение

конца

результирующей

 

 

последовательности

 

 

transform(b1, e1, b2, b3, bop)

применяет бинарный оператор bop к двум

 

 

последовательностям, начинающимся в b1

 

 

и b2, для создания последовательности b3;

 

 

возвращает

положение

конца

 

 

результирующей последовательности

 

replace (b, e, t1, t2)

в диапазоне от b до e замещает значение t1

 

 

на t2

 

 

 

replace_if (b, e, p, t2)

в диапазоне от b до e замещает значение,

 

 

удовлетворящие предикату p, на t2

 

replace_copy(b1, e1, b2, t1,

копирует диапазон от b1 до e1 с заменой t1

t2 )

 

на t2; результат размещается с адреса b2

replace_copy_if ( b1, e1, b2,

копирует диапазон от b1 до e1 с заменой

p, t2 )

 

элементов, удовлетворяющих предикату p,

 

 

на t2; результат размещается с адреса b2

remove (b, e, t )

стирает элементы со значением t

 

remove_if,

remove_copy,

стирает элементы со значением t подобны

remove_copy_if

replace, но значения стираются, а не

 

 

копируются

 

 

 

fill( b, e, t )

 

заполняет значением t диапазон от b до e

generate (b, e, gen )

c помощью вызова генератора gen

 

 

присваивает значения диапазону от b до e

rotate (b , m , e )

поворачивает влево элементы в диапазоне

 

 

от b до e;

 

 

 

random_shuffle (b , e )

перемешивает элементы

 

 

 

 

109

 

 

 

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