|
Программирование >> Разработка устойчивых систем
++it: c.insertdt. сЗ.beginO. сЗ.епсЮ); printCc, с after insertC it. сЗ.beginO. c3.end()) ): it = C.beginO: ++it: c.erase(it): printCc. c after erase(it) ): typename Ci::iterator it2 = it = c.beginO: ++it: ++it2: ++it2: ++it2: ++it2: ++it2: c.erase(it. it2): printCc, c after erase(it. it2) ): c.swap(c2): printCc. c after swap(c2) ): c.clearO: print(c. c after clearO ): int mainO { basicOps<vector<int> >( vector ): basicOps<deque<int> >( deque ): basicOps<list<int> >( list ): } III- Первый шаблон функции print() выводит основную информацию, которую можно получить у любого последовательного контейнера: наличие элементов в контейнере, его текущий размер, максимально возможный размер контейнера, начальный и конечный элементы. Также из листинга видно, что каждый контейнер содержит функции begin() и end(), возвращающие итераторы. Функция basicOps() тестирует все остальное (и в свою очередь, вызывает print()), включая разнообразные конструкторы (конструктор по умолчанию, копирующий конструктор), функции получения начального и конечного итераторов. Также тестируется оператор присваивания = и две разновидности функции assign класса контейнера. Одна функция получает количество элементов и исходное значение, а вторая - начальный и конечный итераторы. Все основные последовательные контейнеры являются обратимыми, о чем свидетельствует использование функций rbegin() и rend(). Вы можете изменить размеры последовательного контейнера или удалить из него все элементы функцией с1еаг(). При изменении размеров контейнера функцией resize() новые элементы инициализируются конструктором по умолчанию для типа элементов или нулями, если они относятся к встроенным типам. Используя итератор для обозначения позиции вставки в любом последовательном контейнере, можно при помощи функции insert() вставить один элемент, несколько элементов с одинаковыми значениями или группу элементов из другого контейнера, заданную начальным и конечным итераторами. Чтобы уничтожить один элемент из середины контейнера функцией erase(), воспользуйтесь итератором; для уничтожения группы элементов требуется пара итераторов. Поскольку список (list) поддерживает только двусторонние итераторы, все перемещения итераторов должны выполняться на уровне инкрементов и декрементов (для векторов и деков, поддерживающих итераторы произвольного доступа, итераторы можно перемещать сразу на несколько позиций операторами + И-). Функции push front() и pop front() в отличие от списков и деков векторами не поддерживаются. С другой стороны, функции push back() и рор Ьаск() работают со всеми тремя типами контейнеров. Функция swapO класса контейнера может вызвать недоразумения, поскольку в стандартной библиотеке присутствует алгоритм swap(), меняющий местами значения двух однотипных объектов. Функция swap() icjiacca контейнера меняет местами содержимое двух контейнеров с однотипными объектами. Операция выполняется эффективно за счет пересылки внутреннего содержимого контейнеров, состоящего в основном из указателей. Алгоритм swap() обычно использует присваивание (дорогостоящая операция для целых контейнеров), но механизм специализации заставляет его вызывать функцию swap() для стандартных контейнеров. Также существует алгоритм iter swap, который с помощью итераторов меняет местами два элемента одного контейнера. В следующих разделах описаны отличительные особенности всех типов последовательных контейнеров. Вектор Шаблон класса vector напоминает усовершенствованный массив, и это сходство не случайное. Он также поддерживает индексацию в стиле массивов, но при этом расширяется динамически. Векторы настолько удобны, что в простейшем виде этот шаблон был представлен в начале первого тома и регулярно использовался в предыдущих примерах. В данном разделе приводится дополнительная информация о векторах. Для достижения максимальной эффективности индексирования и перебора элементы вектора хранятся в одном непрерывном блоке. Данное обстоятельство очень важно для понимания базовых особенностей вектора. Из него следует, что индексирование и перебор выполняются с молниеносной быстротой - практически так же быстро, как в массиве объектов. Но с другой стороны, вставка новых объектов в любой позиции, кроме конечной, выполняется крайне медленно. Кроме того, когда в векторе кончается заранее выделенная свободная память, ему приходится выделять новый (увеличенный) блок памяти и копировать в него все элементы. Такой подход ведет к ряду неприятных побочных эффектов. Издержки на выделение дополнительной памяти При создании вектор выделяет блок памяти, как бы предполагая, сколько объектов вы собираетесь хранить в нем. Пока количество объектов не превышает размеров изначально выделенного блока, все операции выполняются быстро (если вы знаете, сколько объектов будет храниться в векторе, можно заранее зарезервировать необходимую память функцией reserve()). Но если произойдет переполнение, вектор выполняет следующие операции. 1. Выделение нового блока памяти большего размера. 2. Копирование всех объектов из прежнего блока памяти в новый (с использованием копирующего конструктора). 3. Уничтожение всех прежних объектов (с вызовом деструктора для каждого объекта). 4. Освобождение прежнего блока памяти. Если вектор переполняется достаточно часто, а содержащиеся в нем объекты достаточно сложны, конструирование копий и уничтожение обходятся достаточно дорого. По этой причине векторы (и контейнеры STL вообще) ориентированы на хранение значений, копирование которых обходится малыми затратами. К этой категории относятся указатели. Давайте посмотрим, что происходит при заполнении вектора. В этом нам поможет упоминавшийся ранее класс Noisy. Этот класс выводит информацию о создании и уничтожении объектов, о присваивании и конструировании копий: : C07:Noisy.h Класс для отслеживания основных операций с объектами #ifndef NOISY H Idefine NOISY H linclude <iostream> using endl: using cout: using ostream: class Noisy { static long create, assign, copycons. destroy: long id: public: NoisyO : id(create++) { cout d[ id ] endl: Noisy(const NoisyS rv) : id(rv.id) { cout c[ id ] endl: ++copycons: NoisyS operator=(const NoisyS rv) { cout ( id )=[ rv.id ] endl: id = rv.id: ++assign: return *this: friend bool operator<(const NoisyS Iv. const NoisyS rv) { return Iv.id < rv.id: friend bool operator==(const NoisyS Iv, const NoisyS rv) { return Iv.id == rv.id: -NoisyO { cout -[ id ] endl: ++destroy: friend ostreamS operator (ostream& os. const Noisy& n) { return OS n.id: friend class NoisyReport: struct NoisyGen { Noisy operatorOO ( return NoisyO: } Синглетный класс. Статистика автоматически выводится при завершении программы: class NoisyReport { static NoisyReport nr: NoisyReportO {} Закрытый конструктор
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |