программирование, создание программ, учебник Delphi, учебник по программированию, бейсек, делфи, си, паскаль
 
 
 

 

Шаблон функции вывода содержимого контейнера


Демонстрация функционирования контейнеров требует часто выводить их содержимое, поэтому будет целесообразно написать шаблон функции, которая выводит содержимое контейнера любого типа. Первый параметр (т& 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 <Туре> изменить порядок сортировки элементов последовательности. Предикатом называется функция, область значений которой есть множество { false, true } или { 0, 1 }. В нашем случае этот предикат пользуется результатом операции operator > (), определенной в классе string. Кроме того, мы показываем, как можно пользоваться шаблоном функций distance, который позволяет определить количество приращений типа dif ference_type между двумя позициями, адресуемыми итераторами. Другой шаблон функций — advance позволяет продвинуться вдоль контейнера на число позиций, задаваемое параметром, который может быть и отрицательным.
Предикаты и функциональные объекты
Предикатом, как определено в курсе математической логики, называется любая функция многих переменных, областью значений которой является множество {false, true} или {0, 1}. Покажем, как можно отсортировать по имени контейнер объектов класса Man, который мы определили в этом уроке выше. Алгоритм sort по умолчанию использует для сортировки бинарное отношение, задаваемое операцией operator< (). Так как в классе Man эта операция была определена в виде метода класса, то алгоритм справится с поставленной задачей. Однако если мы захотим изменить порядок и отсортировать последовательность объектов по возрасту, то нам придется воспользоваться другим отношением. Решить эту задачу можно двумя способами:

  • использовать свою собственную функцию-предикат, которая определяет порядок следования объектов;
  • использовать конструкцию, называемую функциональным объектом.

Первый способ реализуется путем создания глобальной функции, на вход которой поступают два сравниваемых объекта, а на выходе должен быть результат их сравнения, например типа bool. Второй способ реализуется созданием функционального объекта (function object или functor), являющегося структурой, в которой определена операция operator (). Этот термин, однако, используется для обозначения не только описанного объекта, но и для любого другого, который можно вызвать так, как вызывают функцию. Собственно, кроме описанного случая, роль функционального объекта может выполнять обычная функция и указатель на функцию.
Покажем, как создать предикат. В описание класса Man следует добавить объявление внешней функции в качестве friend-объекта, так как в ее теле будут анализироваться private-данные класса Man. Добавьте в класс Man такое описание:
//======== Предикат, задающий отношение порядка
friend bool LessAge (Mans a, Man& b);
Затем вставьте коды этой функции после объявления класса, но до тела функции main:
bool LessAge (Man& a, Man& b)
{
//======== Сравниваем по возрасту
return a.m_Age < b.m_Age;
}
Теперь можно создать контейнер объектов класса Man и убедиться в возможности его сортировки двумя способами. В момент создания контейнер может быть инициализирован элементами обычного массива. Ниже мы показываем, как это сделать:
void main ()
{
//======== Массив объектов класса Man
Man ar[] =
{
Man("Mary Poppins",36),
Man("Joe Doe",30),
Man("Joy Amore",18),
Man("Zoran Todorovitch",27)
};
uint size = sizeof(ar)/sizeof(Man);
//======== Создаем контейнер на основе массива
vector<Man> men(ar, ar+size); pr(men,"Man Vector");
//======== Реверсируем обычный массив
reverse(ar, ar+size);
cout « "\n\tAfter reversing the array\n\n";
for (uint i=0; i<size; i++)
cout « i+1 « ". " « ar[i] « '\n';
//======== Сортиуем по умолчанию
sort (men.begin (), men.endO);
pr(men,"After default sort");
//======== Используем предикат
sort (men .begin () , men.endO, LessAge);
pr(men,"After predicate LessAge sort");
cout « "\n\n";
}
Алгоритм переворота последовательности (reverse) может работать как с контейнером, так и с обычным массивом. Для успешной работы ему надо задать диапазон адресов (range). Обратите внимание на то, что в качестве конца последовательности этому алгоритму, как и многим другим в STL, надо подать во втором параметре адрес ar+size, выходящий за пределы массива. Как объяснить тот факт, что шаблон функции reverse, требуя в качестве параметров переменные типа iterator, тем не менее работает, когда ему подают обычный указатель типа Man*? В документации вы можете найти такое объяснение. Указатель — это итератор, но итератор — это не указатель. Итератор — это обобщение (generalization) указателя.
Результат работы программы выглядит так:
Man Vector # Sequence:
1. Mary Poppins, Age: 36
2. Joe Doe, Age: 30
3. Joy Amore, Age: 18
4. Zoran Todorovitch, Age: 27
After reversing the array
1. Zoran Todorovitch, Age: 27
2. Joy Amore, Age: 18
3. Joe Doe, Age: 30
4. Mary Poppins, Age: 36
After default sort # Sequence:
1. Joe Doe, Age: 30
2. Joy Amore, Age: 18
3. Mary Poppins, Age: 36 /
4. Zoran Todorovitch, Age: 27
After predicate LessAge sort # Sequence:
1. Joy Amore, Age: 18
2. Zoran Todorovitch, Age: 27
3. Joe Doe, Age: 30
4. Mary Poppins, Age: 36
Здесь мы убрали сообщения, которые выводит деструктор класса Man. Они носят чисто учебный характер. В частности, они позволяют обозначить те моменты из жизни объектов класса Man, когда они уничтожаются, то есть когда система вызывает для них деструктор.
Сейчас мы намерены создать функциональный объект и использовать его для выбора режима сортировки по имени или по возрасту. Текущий выбор удобно хранить в static-переменной класса Man. Такие переменные, как вы знаете, являются общими для всех объектов класса. Изменяя их значение, можно управлять общими установками, касающимися всех объектов класса. Мы будем управлять текущим режимом сортировки. Для удобства чтения кода введем глобальное определение типа SORTBY - перечисление режимов сортировки:
enum SORTBY { NAME, AGE }; // Режимы сортировки
Декларацию static-переменной следует вставить в public-секцию класса Man:
static SORTBY m_Sort; // Текущий режим сортировки
Определение static-переменной, согласно законам ООП, должно быть глобальным:
//======= Определение и инициализация
SORTBY Man::m_Sort = NAME;
Сам функциональный объект должен быть объявлен как внешняя глобальная friend-конструкция. Вставьте следующее объявление внутрь класса Man:
//======= Функциональный объект имеет доступ к данным
friend struct ManLess;
Затем объявите сам функциональный объект, который, надо признать, имеет несколько непривычный вид:
//======== Функциональный объект
struct ManLess
{
bool operator()(Man& a, Man& b)
{
return a.m_Sort==NAME ? (a.m_Name < b.m_Name)
: (a.m_Age < b.m_Age);
}
};
Как вы видите, он имеет вид структуры (возможен и класс), в которой определена единственная операция operator (). В нем мы анализируем текущее значение флага сортировки и в зависимости от него возвращаем результат сравнения двух объектов, поступивших в качестве параметров, по тому или иному полю. Использование функционального объекта иллюстрируется следующим кодом, который вы можете вставить в конец существующей функции main:
//========= Используем функциональный объект
Man::m_Sort = NAME;
//========= Сортируем по имени
sort (men .begin (), men.end(), ManLess ());
pr(men,"After function object name sort");
Man::m_Sort = AGE;
//========= Сортируем по возрасту
sort (men. begin (), men.end(), ManLess ());
pr(men,"After function object age sort");
Аналогично предикату greater<Type>, который мы уже использовали, в STL определен предикат less<Type>, который обеспечивает упорядочивание контейнера, задаваемое операцией operator< (). Но, если вы вставите в функцию main такой код:
//========= Используем стандартный предикат
sort(men.begin(), men.end(),less<Man>() ) ;
pr(men,"After less<Man> sort");
то получите сообщение об ошибке, так как он будет искать реализацию operator< () в виде внешней функции с двумя сравниваемыми параметрами. Напомню, что мы уже реализовали эту операцию, но в виде метода класса с одним параметром. Для решения проблемы вы можете, не убирая старой версии, вставить новую. Декларируйте в классе Man внешнюю friend-функцию:
//========= Нужна для предиката less<Man>()
friend bool operator< (const Man& a, const Man& b);
Затем дайте внешнее тело этой функции. Отношение порядка здесь намеренно изменено по сравнению с предыдущей реализацией operators (). Как оказалось, обе версии будут работать в различных ситуациях. Первая — при сортировке по умолчанию, а вторая — при сортировке предикатом less<Man> .
bool operator<(const Man& a, const Man& b)
{
//======== Сравниваем по возрасту
return a.m_Age < b.m_Age;
}
Проверьте результат, запустив приложение. Проследите, чтобы в main был при этом код с вызовом алгоритма сортировки с тремя Параметрами:
sort(men.begin (), men.end(),less<Man>());
Здесь же уместно добавить, что в STL есть шаблоны, которые называются negators (отрицатели). Шаблон not2, например, позволяет инвертировать результат бинарной операции. Вставьте в конец функции main следующий фрагмент:
//========= Используем отрицатель бинарной операции
sort(men.begin (), men.endf), not2 (less<Man>()));
pr(men,"After not2(less<Man>) sort");
и убедитесь в том, что последовательность отсортирована теперь по убыванию возраста.
  
Поиск с помощью предиката
Поиск первого объекта, который удовлетворяет условию, заданному предикатом, осуществляется с помощью шаблона функции f ind_if. В качестве третьего, параметра она требует задать имя функции-предиката. Введите в состав класса объявление такой функции:
//========= Предикат принадлежности к teenager
friend bool Teen (Man& m);
Тело этой функции определите глобально, то есть вне класса:
//========= Предикат принадлежности к teenager
bool Teen(Man& m)
{
return 13 < m.m_Age && m.m_Age < 19;
}
Теперь покажем, как искать в контейнере первый элемент, удовлетворяющий предикату, а также все элементы, удовлетворяющие этому условию. Ниже нам понадобятся несколько объектов класса Man, поэтому мы ввели их объявление в начало функции main. Далее везде мы будем считать, что эти объекты присутствуют в функции main, но не будем приводить их заново:
void main ()
{
//======== Набор объектов класса Man
Man joe("Joe Doe",30),
joy ("Joy Amore", 18) ,
Mаrу("Mary Poppins",36),
duke("Duke Ellington",90),
liza("Liza Dale", 17),
simon("Simon Paul",15),
zoran("Zoran Todorovitch",27) ,
Charlie("Charlie Parker",60),
win("Winton Kelly",50),
mela("Melissa Robinson",9);
vector<Man> men;
men.push_back (zoran);
men.push_back (liza);
men.push_back (simon);
men.push_back (mela);
// Поиск первого объекта, удовлетворяющего предикату
vector<Man>::iterator p =
find_if (men .begin () ,
men.endO, Teen);
//======== Ручной поиск всех таких объектов
while (p != men.end())
{
cout « "\nTeen: " « *p;
p = find_if(++p, men.endO, Teen);
}
cout « "\nNo more Teens\n";
//======== Подсчет всех teenagers
uint teen = count_if (men.begin (),men.endO , Teen);
cout « "\n\n Teen totals: " « teen;
//======== Выполняем функцию для всех объектов
for_each(men.begin(),men.end(),OutTeen) ;
//======== Используем обратный итератор
cout«"\n\nMan in reverse\n";
for (vector<Man>::reverse_iterator
r = men.rbegin();
r != men.rendO; r++) cout«*r«";
//======== Заполняем вектор целых
vector<int> v;
for (int i=l; i<4; i++) v.push_back(i);
//======== Иллюстрируем алгоритм и адаптивный functor
transform(v.begin () , v.end(), v.begin (), negate<int> () ) ;
pr(v,"Integer Negation");
//======== Создаем еще два вектора целых
vector<int> vl(v.size()), v2 (v.size());
//======== Иллюстрируем алгоритм заполнения вектора
fill (vl.begin (), vl.endO, 100);
//======== Иллюстрируем проверку
assert (vl .size () >= v.size() && v2.size() >= v.sizeO);
//======== Иллюстрируем вторую версию transform
transform(v.begin(), v.end(), vl.begin(), v2.begin(),

plus<int>() ) ;
pr(v2,"Plus");
cout « "\n\n";
}
В рассмотренном фрагменте мы иллюстрируем использование алгоритма count_if, который проходит по заданному диапазону последовательности и возвращает количество объектов, удовлетворяющих предикату. Алгоритм f or_each позволяет выполнить определенное действие для заданного диапазона последовательности. В примере функция OutTeen вызывается для всех элементов контейнера. Приведем тело этой функции:
void OutTeen(Man& m)
{
// Если парамтр удовлетворяет предикату, то выводим его
if (Teen(m))
cout « "\nTeen: " « m;
}
Далее в коде демонстрируется, как использовать обратный итератор reverse_ iterator. Для него справедливы позиции rbegin — последний элемент последовательности и rend — барьер, ограничивающий последовательность спереди. Операция ++ сдвигает итератор на один элемент в сторону к началу последовательности.
Последний фрагмент функции main демонстрирует использование алгоритма transform, который воздействует на каждый элемент указанного диапазона и модифицирует его в соответствии либо с unary function (первая версия), либо с binary function (вторая версия). Суть модификации определяется последним параметром. В нашем случае мы используем negator (отрицатель) и бинарную операцию plus, настроенную на тип int. Сложить два контейнера можно и другими способами.
Если вы подключите файл заголовков <assert. h>, то сможете осуществлять логические проверки с помощью функции assert. Она проверяет свой аргумент и, если он равен false, выводит на экран сообщение вида:
Assertion failed: s.topQ == joy,
file C:\My ProjectsXStack.cpp, line 29
abnormal program termination
Затем прекращает процесс вызовом функции abort. Если результатом выражения (аргумента функции assert) будет true, то выполнение продолжается.
  
Связыватели и адаптеры
* Связывателями (binders) называются вспомогательные шаблоны функций, которые создают некий объект (adaptor) , подстраивающий или преобразующий бинарный функциональный объект в унарный путем привязывания недостающего аргумента. Звучит запутанно, но суть достаточно проста. Представьте, что надо найти в нашей последовательности людей первого человека, который моложе, чем
Man win("Winton Kelly", 50);
Для объектов класса Man уже определена бинарная операция operator< (), которой пользуется предикат less<Man> (), и мы показали использование этого предиката в алгоритме сортировки по возрасту. В том примере функция sort сама подставляла оба параметра в бинарную функцию operator< (), сравнивая объекты для нужд сортировки. Теперь мы используем связыватель bind2nd, для того чтобы зафиксировать (привязать) второй параметр этой функции и сделать его равным объекту win. Первый параметр при этом остается свободным, он будет пробегать по всему контейнеру. Таким образом, мы сможем сравнить все объекты последовательности с одним фиксированным объектом win.
В этом же фрагменте мы покажем, как использовать другой адаптер mem_f un_ref, который тоже является вспомогательным шаблоном функции для вызова какой-либо функции, являющейся членом класса, в нашем случае Man. Вызов осуществляется для всех объектов класса в процессе прохода по контейнеру. Введите в состав класса Man две public-функции, выделяющие имя и фамилию человека. В коде этих функций попутно демонстрируются методы класса string, которые позволяют осуществлять поиск и выделение подстроки:
//======== Выделяем имя
Man FirstName()
{
//======== Ищем первое вхождение пробела
int pos = m_Name.find_first_of(string(" "),0);
string name = m_Name.substr(0, pos);
cout « '\n' « name;
return *this;
}
//======== Выделяем фамилию
Man SurName()
{
//======== Ищем последнее вхождение пробела
int pos = m_Name.find_last_of(" "), num = m_Name.length () - pos;
string name = m_Name.substr(pos + 1, num);
cout « '\n' « name; return *this;
}
Вектор заполняется элементами, взятыми из массива а г, и при этом используется метод assign, который стирает весь массив и вновь заполняет его элементами, копируя их из диапазона памяти, задаваемого параметрами. Далее мы показываем, как используется связыватель bind2nd и адаптер члена-функции mem_f un_ref:
void main ()
{
Man ar[] =
{
joy, joe, zoran, тагу, simon, liza, Man("Lina Groves", 19)
};
uint size = sizeof(ar)/sizeof(Man);
vector<Man> men;
men.assign(ar, ar+size);
pr(men,"Man Vector");
//======= Привязка второго аргумента
vector<Man>::iterator p = find_if(men.begin(),
men.end(), bind2nd(less<Man>(), win));
if (p != men.end())
cout « "\nFound a man less than " « win « "\n\t" « *p;
//======= Использование метода класса (mem_fun_ref)
cout « "\n\nMen Names:\n";
for_each (men.begin(), men.end(), mem_fun_ref(&Man::SurName));
cout « "\n\nMen First Names:\n";
for_each (men.begin (), men.end(), mem_fun_ref(&Man::FirstName));
cout « "\n\n";
}
Напомним, что для успешной работы вы должны вставить в функцию main тот набор объектов класса Man, который был приведен ранее.
Примечание
При анализе этого кода бросается в глаза неестественность прототипов функций SurName и FirstName. Логика использования этих функций совсем не требует возвращать какое-либо значение, будь то Man, или переменная любого другого типа. Естественным выбором будет прототип void SurNameQ;. Но, к сожалению, этот выбор не проходит по неизвестным мне причинам ни в Visual Studio б, ни в Studio.Net 7.O. Я достаточно много времени потратил на бесполезные поиски ответа на этот вопрос и пришел к выводу, что это ошибка разработчиков. В подтверждение такого вывода приведу следующие аргументы. Во-первых, измените тип возвращаемого значения на любой другой, но не void, и программа будет работать. Например, возьмите прототип string SurName(); и возвращайте return "MicrosoftisOK"; (или другую пару: int и-127). Во-вторых, все примеры на (mem_fun_ref) в документации MSDN возвращают загадочный bool. В-третьих, в документации SGI (Silicon Graphics) приведены аналогичные примеры с функциями, возвращающими void. Там, как вы знаете, используется другая платформа (IRIS). В-четвертых, наш пример (без void) проходит в Visual Studio б и не работает в бета-версии Studio.Net. Будем надеяться, что ситуация со временем выправится.
Адаптер mem_fun в отличие от mem_fun__ref используется с контейнерами, хранящими указатели на объекты, а не сами объекты. Хорошим примером использования mem_f un, в котором иллюстрируется полиморфизм позднего связывания (late binding polymorphism), является следующий:
//======== Базовый класс. К сожалению, абстрактным
//======= его не позволит сделать контейнер
struct Stud
virtual bool print()
{
cout « "\nl'm a Stud";
return true;
}
};
//===== Производный класс struct GoodStud : public Stud
{
bool print ()
{
cout « "\nl'm a Good Stud";
return true;
}
};
//======= Еще один производный класс
struct BadStud : public Stud
{
bool print ()
{
cout « "XnI'm a Bad Stud";
return true;
}
};
//======= Иллюстрируем полиморфизм в действии
void main () {
//====== Вектор указателей типа Stud*
vector<Stud*> v;
//====== Они могут указывать и на детей
v.push_back (new StudO);
v.push_back (new GoodStudO);
v.push_back(new BadStud(J);
//====== Выбор тела метода происходит поздно
//====== на этапе выполнения
for_each(v.begin(), v.end(), mem_fun(&Stud:: print));
cout <<"\n\n";
}
Конечно же, эта программа выведет:
I'm a Stud
I'm a Good Stud
I'm a Bad Stud
так как mem_fun будет вызвана с помощью указателя типа stud* (на базовый класс) — непременное условие проявления полиморфизма, то есть выбора конкретной версии виртуальной функции (адреса функции из vtable) на этапе выполнения. Выбор определяется конкретной ситуацией — типом объекта, попавшим под родительский перст (указатель) в данный момент времени.
  
Последовательности типа deque
Контейнер типа deque (очередь с двумя концами) похож на vector в том смысле, что допускает выбор элемента по индексу и делает это быстро. Отличие состоит в том, что он умеет эффективно вставлять новые элементы как в конец, так и в начало последовательности. Deque не имеет некоторых методов, которые имеет vector, например capacity и reserve. Вместо этого он имеет методы, которых нет у вектора, например push_f ront, pop_back и pop_f ront. Далее мы будем исследовать возможности различных контейнеров, и каждый новый контейнер требует подключения своего файла заголовков. В данный момент не забудьте вставить директиву препроцессора tinclude <deque>:
void main ()
{
deque<double> d;
d.push_back(0.5) ;
d.push_back(l.);
d.push_front(-1.);
pr(d,"double Deque");
//======== Ссылки на два крайних элемента
deque<double>::reference
rf = d.front(),
rb = d.back();
//======== Присвоение с помощью ссылок
rf = 100.;
rb = 100.;
pr(d,"After using reference");
//======== Поиск с помощью связывателя
deque<double>::iterator p = find_if(d.begin(), d.end(),
bind2nd(less<double>(),100.));
//======== Вставка в позицию перед позицией,
//======== на которую указывает итератор
d.insert(p,-1.);
pr(d,"After find_if and insert");
//======== Второй контейнер
deque<double> dd(2,-100.);
//======== Вставка диапазона значений
d.insert (d.begin ()+1, dd.begin(), dd.end());
pr(d,"After inserting another deque");
cout«"\n\n";
}
Следующий фрагмент демонстрирует, как можно копировать контейнеры (сору) и обменивать данные между ними (swap). Шаблон функций find позволяет найти объект в любой последовательности. Он откажется работать, если в классе объектов не определена операция operator== (). Отметьте также, что после вставки или удаления элемента в контейнер типа deque все итераторы становятся непригодными к использованию (invalid), так как произошло перераспределение памяти. Однако удаление с помощью pop_back или pop_f ront портит только те итераторы, которые показывали на удаленный элемент, остальные можно использовать. При использовании фрагмент надо дополнить объявлениями объектов класса Man:
void main ()
{
deque<Man> men;
men.push_front (Man("Jimmy Young",16));
men.push_front (simon);
men.pushjoack (joy);
pr(men,"Man Deque");
//======== Поиск точного совпадения
deque<Man>::iterator p =
find(men.begin(),men.end() , joy);
men.insert(p,тагу);
pr(men,"After inserting тагу");
men.pop_back(); men.pop_front ();
pr(men,"After pop_back and pop_front");
p = find(men.begin(),men.end(),joy);
if (p == men.end())
cout « '\n' « joy « " not found!";
men.push_front(win); men.push_back(win);
pr(men,"After doubly push win");
//======== Второй контейнер
deque<Man> d(3,joy); men.resize(d.size ());
//======== Копируем d в men
copy(d.begin(), d.end(), men.begin()); pr(men,"After resize and copy");
//======== Изменяем контейнер
d.assign(3,win);
//======== Обмениваем данные
d.swap(men);
pr(men,"After swap with another deque"); cout«"\n\n";
}

 

 
На главную | Содержание | < Назад....Вперёд >
С вопросами и предложениями можно обращаться по nicivas@bk.ru. 2013 г. Яндекс.Метрика