2vcTnguyvU
.pdfvoid 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 |
|
|
|