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

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 возвращает первый

элемент очереди

value_type& back() const возвращает последний

элемент очереди

bool empty () const возвращает истину,

если очередь пуста

size_type size() const возвращает число элементов

в очереди

орегасог== и operator < равенство и лексикографическое

меньше чем

Давайте адаптируем стек из реализации vector. Обратите внимание, как АТД из STL заменяют наши отдельно разработанные реализации типов.

В файле stl_stak.cpp

//Адаптируем стек из вектора

#include <iostream>

#include <stack>

#include <vector>

#include <string>

using namespace std;

int main()

{

stack<string, vector<string> > str_stack;

string quote[3] =

{"Громче всех скрипит именно то колесо,\n", "которое смазывают\n", "Джош Биллингс \n" };

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

str_stack.push(quote[i] ) ;

while (!str_stack.empty()) {

cout « str_stack.top();

str_stack.pop();

}

}

Итераторы

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

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

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

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

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

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

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

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

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

Итератор istream_iterator происходит от итератора ввода и работает исключи­тельно с чтением из потоков. Итератор ostream_iterator происходит от итерато­ра вывода и работает исключительно с записью в потоки. Напишем программу, кото­рая запрашивает пять чисел, считывает их и вычисляет их сумму, и в которой ввод-вывод использует эти итераторы. Шаблон для istream_iterator инстанцируется как <тип, расстояние>. Это расстояние обычно задается как ptrdiff_t. Как определено в cstddef или stddef.h, это целый тип, представляющий разницу между двумя значениями указателей.

В файле stl_io.cpp

//Использование istream_iterator и ostream_iterator

#include <iterator>

#include <iostream>

#include <vector> using namespace std;

int main()

{

vector<int> d(5);

int i, sum;

istream_iterator<int, ptrdiff_t> in(cin);

ostream_iterator<int> out(cout, "\t");

cout « "введите 5 чисел " « endl;

sum = d[0] = *in; //ввод первого значения

ford = 1; i < 5; ++i) {

d[i] = *++in; //ввод остальных значений

sum + = d [ i ] ; '

}

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

*out = d[i] ; //вывод последовательных значений

cout « " Сумма = " « sum « endl;

}

Итератор istream_iterator инстанцируется типом int и параметром ptrdiff_t. Тип ptrdiff_t является типом-расстоянием, который итератор ис­пользует для перехода к следующему элементу. В приведенном выше объявлений конструктору in передается входной поток с in. Оператор автоинкремента продви­гает in и читает следующее значение типа int из указанного входного потока. Кон­структору итератора вывода out передается выходной поток cout и ограничитель " \t" типа char*. To есть в поток cout после каждого записанного целого значения будет вставляться символ табуляции. В этой программе при разыменовании итерато­ра out присвоенное целое значение записывается в cout.

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

Итераторы могут быть адаптированы для обеспечения обратного просмотра и про­смотра со вставкой. Обратные итераторы изменяют порядок итерации на проти­воположный; с помощью итераторов вставки вместо обычного режима перезаписи выполняется вставка. В следующем примере для прохода по последовательности ис­пользован обратный итератор.

В файле stl_iadp.cpp

//Использование обратного итератора

#include <iostream>

#include <vector>

using namespace std;

template <class ForwIter>

void print(ForwIter first, ForwIter last, const char* title)

{

cout « title « endl;

while ( first != last)

cout « *first++ « '\t';

cout « endl;

}

int main()

(

int data[3] = { 9, 10, 11);

vector<int> d(data, data + 3);

vector<int>::reverse_iterator p = d.rbegin();

print(d.begin(), d.end(), "Исходный");

print(p, d.rend(), "Обращенный");

}

Эта программа использует обратный итератор для изменения направления, в кото­ром функция print () выводит элементы вектора d.

Другие алгоритмы из библиотеки iterator обсуждаются в разделе Е.2.4, «Адаптеры итераторов», на стр.427.

Алгоритмы

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

Категории библиотеки алгоритмов STL

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

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

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

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

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

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

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

В файле stl_sort.cpp

//Использование sort() из STL

#include <iostream>

#include <algorithm>

using namespace std;

const int N = 5;

int main()

{

int d [N] , i , *e = d + N;

for (i = 0; i < N; ++i)

d[i] = rand() ;

sort(d, e) ;

for (i = 0; i < N; ++i)

cout << d[i] << '\t' ;

}

Это простое применение алгоритма библиотеки sort, оперирующего встроенным массивом d [ ]. Заметьте, как значения обычного указателя могут быть использован в качестве итераторов.

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

• template <class RandAcс>

void sort(RandAcc b, RandAcc e);

Это алгоритм быстрой сортировки элементов от Ь до e. Итератор RandAcc должен быть итератором произвольного доступа.

Другие прототипы алгоритмов можно найти в разделе Е.3.1, «Алгоритмы сортировки», на стр. 428.

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

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

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

В файле stl_find.cpp

//Использование функции поиска

#include <iostream>

#include <algorithm>

#include <string>

using namespace std;

int main()

{ //слова

string words[5] = ( "мой", "пой", "дрыг", "твой", "прыг"};

string* where;

where = find(words, words + 5, "пой");

cout « *++where'« endl; //дрыг sort(words, words + 5);

where = find(words, words + 5,- "пой");

cout « *++where « endl; //прыг

}

Здесь find () используется для поиска позиции слова «пой». До и после сортировки массива words [ ] выводится слово, идущее за «пой».

Вот библиотечные прототипы для двух алгоритмов поиска:

• template<class Inputlter, Class T>

Inputlter find(Inputlter b, Inputlter e, const T& t);

Ищется позиция t в диапазоне от b до e.

• template<class Inputlter, Class Predicate>

Inputlter find(Inputlter b, Inputlter e. Predicate p) ;

В диапазоне от b до e ищется позиция первого элемента, который сделает предикат (predicate) истинным, в противном случае возвращается позиция e.

Другие прототипы неизменяющих алгоритмов даны в разделе Е.3.2, «Не изменя­ющие последовательность алгоритмы», на стр. 431.

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

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

reverse () и copy().

В файле stl_revr.cpp

//Использование изменяющих функции reverse и copy

#include <iostream>

#include <algorithm>

#include <string>

#include <vector> using namespace std;

int main ()

{

string first_names[5] = ("лаура", "айра", "болтушка", "дебра", "дед"};

string last_names[5] = ("пол", "пол", "долсберри", "долсберри", "мороз"};

vector<string> names(first_names, firsfc_names + 5);

vector<string> names2(10);

vector<string>::iterator p;

copy(last_names, last_names + 5, names2.begin());

copy(names.begin(), names.end(), names2.begin() + 5);

reverse(names2.begin(), names2.end() ) ;

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

}

Первый вызов изменяющей функции copy () помещает last_names в контейнер-вектор names2. Второе обращение к функции copy () копирует в first_names то, что было использовано при создании вектора names. Функция reverse () обращает все элементы, которые затем выводятся.

Вот библиотечные прототипы для двух алгоритмов копирования:

• template<class Inputlter, class Outputlter>

Outputlter copy(Inputlter bl, Inpufclter el, Outputlter b2) ;

Это алгоритм копирования элементов от b1 до el. Копия размещается начиная с Ь2.-Возвращаемая позиция является концом копии.

• template<class Bidilteri, class Bidilter2> :

Bidilter2 copy„backward(Bidilteri bl, Bidilteri el, Bidilter2;b2) ;

Это алгоритм копирования элементов от bl до el. Копия размещается начиная с b2.' Копирование, запускаемое «наоборот», от е1 к b2, выполняется в обратном направ­лении («левее» b2). Возвращается позиция b2 - (e1 – b1).

Другие алгоритмы даны в разделе Е.3.3, «Изменяющие последовательность алгоритмы», на стр. 432.

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

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

В следующей программе численная функция accumulate () выполняет суммирование вектора, a inner_product () подсчитывает скалярное произведение.

В файле stl_numr.cpp

//Суммирование и перемножение векторов

#include <iostream> ^include <numeric> using namespace std;

int main() {

double v1[3] = {1.0, 2.5, 4.6},

v2[3] = {1.0, 2.0, -3.5},

double sum, inner_p;

sum = accumulate(v^, v1 + 3, 0.0);

inner_p = inner_product(v1, v1 + 3, v2, 0.0);

cout « "Сумма = " « sum

« ", скалярное произведение = " « inner_p « endl;

}

Эти функции ведут себя так, как должны вести себя численные типы, для которых определены операции + и *.

Вот библиотечные прототипы для двух накапливающих алгоритмов:

• template<class InputIter, class T>

Т accumulate(InputIter b, InputIter e, T t);

Это стандартный алгоритм накопления, причем за начальное значение суммы принимается t. К этой сумме последовательно добавляются элементы из диапазо­на от b до e.

• template<class InputIter, class T, class Bin0p>

T accumulate(InputIter b. InputIter e, T t, BinOP bop);

Это алгоритм накопления, причем начальное значение равно t. К этому значению последовательно «прибавляются» элементы из диапазона от b до e, но вместо сложе­ния применяется заданная бинарная операция bop.

Другие алгоритмы даны в разделе Е.3.4, «Численные алгоритмы», на стр.434.

Функции

Для дальнейшего освоения библиотеки STL полезны объекты-функции. Например, многие из рассматривавшихся ранее численных функций предполагают применение + или *, но также имеет форму, в которой в качестве аргумента может передаваться заданный пользователем бинарный оператор. Ряд объектов-функций можно найти в библиотеке function, но их можно создать и самостоятельно. Объекты-функции — это классы, в которых определен operator (). Они являются встроенными' и компили­руются так, чтобы получить эффективный код.

' Речь, разумеется, идет о встраивании функции в место вызова (inline), а не о том, что некая конструкция встроена в язык или в его реализацию (built-in). — Примеч. перев.

Арифметические объекты часто используются в численных алгоритмах, например accumulate (). Сравнивающие объекты нередко применяются в алгоритмах сортировки, таких как merge (). Расширенные таблицы для трех типов объектов–функции даны в разделе Е.4, «Функции», на стр. 435.

Адаптеры функций

Адаптеры функций делают возможным создание объектов-функций с помощью адаптирования.

Адаптеры функций

• отрицающий адаптер' — для отрицания предикативных объектов

• связывающий адаптер2 — для связывания аргументов функции

• адаптеры для указателя на функцию3

В следующем примере мы используем связывающую функцию bind2nd для превра­щения начальной последовательности значений в последовательность удвоенных значений.

В файле stl_adap.cpp

//Использование адаптера функции bind2nd

#include <iostream> ^include <algorithm> #include <functional>

#include <string>

using namespace std;

template <class Forwlter>

void print(Forwiter first, Forwiter last,

const char* title)

{

cout << title << endl;

while ( first != last)

cout << *first++ << '\t' ;

cout « endl;

}

int main ( )

{

int data[3] = { 9, 10, 11);

print(data, data + 3, "Исходные значения");

transform(data, data + 3, data,

bind2nd(times<int>(), 2));

print(data, data + 3, "Новые значения");

}

Соседние файлы в папке Тельминов (мб)