Контейнеры библиотеки STL. Последовательности типа vector.
Шаблон функции вывода содержимого контейнера
Демонстрация функционирования контейнеров требует часто выводить их содержимое, поэтому будет целесообразно написать шаблон функции, которая выводит содержимое контейнера любого типа. Первый параметр (т& v) функции рг () задает тип контейнера. Он же является параметром шаблона. Второй параметр (string s) задает строку текста (заголовок), который будет выведен в начале блока данных контейнера:
//===== Шаблон функции для вывода с помощью итератора template <class T> void pr(T& v, string s) { cout <<"\n\n\t"<<s<<" # Sequence: \n"; //====== Итератор для любого контейнера Т::iterator p; int i; for (p = v.begin(), i=0; p!= v.end(); p++, i++) cout << endl << i + 1 <<". "<< *p; cout << '\n'; }
Для пробега по всем элементам любого контейнера используется обобщенный, или абстрактный, указатель, который объявлен как т:: iterator. С помощью итератора, так же как и с помощью обычного указателя, можно получить доступ к элементу, используя операции *, ›. К нему также применима операция ++ – переход к следующему элементу последовательности, но в отличие от указателя с итератором не связана адресная арифметика. Вы не можете сказать, что значение итератора изменится на 4 байта при переходе к следующему элементу контейнера целых чисел, хотя для некоторых типов контейнеров это так и будет. Заметьте, что операция ++ в применении к итератору позволяет перейти к следующему элементу как вектора – элементы расположены в памяти подряд, так и списка – элементы расположены в памяти не подряд. Поэтому итератор – это более сложный механизм доступа к данным, чем простой указатель. Полезно представить итератор в виде рабочего, приставленного к контейнеру и призванного перебирать его элементы.
Возможное присвоение p = v .end (); не означает, что итератор устанавливается на последний элемент последовательности. Вы помните, какую роль играет ноль для обычного указателя при работе с динамическим списком? Примерно такую же роль для итератора выполняет значение v .end () – конец последовательности. Его можно представить в виде итератора, указывающего на воображаемый элемент, следующий за последним элементом контейнера (past-the-end value). Однако инициализатор p = v.begin (); устанавливает итератор в точности на первый элемент последовательности.
Вектор объектов класса
Следующий фрагмент демонстрирует работу с вектором строк типа string. Теперь в контейнере будут храниться объекты класса string, который также является составной частью STL. Этот класс содержит ряд замечательных методов, которые позволяют легко и удобно работать со строками символов произвольной длины. В частности, вы можете складывать строки – осуществлять их конкатенацию, искать подстроки, удалять и заменять подстроки:
#include <vector> #include <string> #include <algorithm> // Для sort и distance #include <functional> // Для greater<string>() tinclude <iostream> using namespace std; void main () { //========= Вектор строк текста vector<string> v; v.push_back("pine apple"); v.push_back("grape"); v.push_back("kiwi fruit"); v.push_back("peach"); v.push_back("pear"); v.push_back("apple"); v.push_back("banana"); //========= Вызываем наш шаблон вывода pr(v, "String vector"); sort (v.begin (), v.end()); pr(v, "After sort"); //========= Изменяем порядок сортировки, на обратный //========= тому, который принят по умолчанию sort(v.begin(), v.end(), greater<string>()); pr(v, "After predicate sort"); cout << "\nDistance from the 1st element to the end: "; vector<string>::iterator p = v.begin (); vector<string>::difference_type d; d = distance(p, v.end()); //========= Отметьте, что end() возвращает адрес //========= за концом последовательности cout << d << endl; cout << "\n\nAdvance to the half of that distanceXn"; advance (p, d/2); cout << "Now current element is: " << *p << endl; d = distance(v.begin (), p); cout << "\nThe distance from the beginning: " << d << endl; d = distance(p, v.begin ()); cout << "\nThe distance to the beginning: " << d << endl; }
Здесь мы демонстрируем, как можно с помощью бинарного предиката greater <Type> изменить порядок сортировки элементов последовательности. Предикатом называется функция, область значений которой есть множество { false, true } или { 0, 1 }. В нашем случае этот предикат пользуется результатом операции operator > (), определенной в классе string. Кроме того, мы показываем, как можно пользоваться шаблоном функций distance, который позволяет определить количество приращений типа dif ference_type между двумя позициями, адресуемыми итераторами. Другой шаблон функций – advance позволяет продвинуться вдоль контейнера на число позиций, задаваемое параметром, который может быть и отрицательным.