Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
STL5 / lab1-vector / lab1-vector.doc
Скачиваний:
12
Добавлен:
10.04.2015
Размер:
183.3 Кб
Скачать

Размер и емкость вектора

Одной из важных особенностей класса vector является его работа с памятью. Размер вектора может динамически изменятся во время выполнения программы, так если при вставке элемента или изменении размера вектора обнаруживается, что для него недостаточно места, происходит автоматическое выделение дополнительной памяти. Кроме этого стандарт гарантирует, что все элементы вектора расположены в одном фрагменте памяти. Это важно для кода взаимодействующего с кодом на С10, а также по соображениям эффективности доступа к произвольному элементу по индексу (положение требуемого элемента в памяти может быть легко вычислено через адрес первого элемента, размер элемента и его индекс).

Такая организация данных класса вектор приводи к следующим особенностям:

  1. При увеличении размера происходит перераспределение памяти, т.е. выделяется кусок памяти достаточный для размещения нового количества элементов, элементы копируются из старого фрагмента памяти в новый, старый фрагмент памяти освобождается. Как правило, при перераспределении памяти выделяется не точное количество необходимое для хранения нового количества элементов, а большее, чтобы в случае последующих вставок не происходило перераспределение памяти. Количество элементов, которое может быть размещено в векторе без перераспределения памяти называется его емкость. Очевидно, что операция перераспределения памяти требует большого количества вычислительных ресурсов, и особенно в случае больших векторов. Частого выполнения перераспределения памяти желательно избегать по соображениям эффективности.

  2. Любая вставка элемента в вектор за исключением добавления элемента в конец требует копирования всех элементов с позиции за позицией вставки до конца вектора, в случае вставки в начало, происходи копирование всех элементов. В случае добавления элемента в конец вектора, он располагается в зарезервированной области памяти или если ее нет, происходит перераспределение памяти. Аналогично операции удаления за исключением удаления из конца вектора требуют копирования элементов, с позиции за удаляемым элементов и до конца вектора. Операции вставки и удаления элементов вектора могут отрицательно сказаться на производительности.

Ниже представлены методы вектора, работающие с размером и емкостью:

template <class T, class A=allocator<T>> class vector

{

public:

//…..

//число элементов

size_type size() const;

// Пуст ли вектор?

bool empty() const

{

return size() == 0

};

//размер самого длинного возможного вектора

size_type max_size() const;

// Изменение размера вектора

// Если размер уменьшается, то «лишние» элементы удалаются,

// если увеличивается, то новые элементы инициализируются значением val.

void resize (size_type sz, T val = T());

// Возвращает емкость вектора – количество элементов, которое может

// содержать vector без перераспределения памяти

size_type capacity() const;

// Резервирует память (и если необходимо производит перераспределение)

// так чтобы вектор мог содержать n элементов без дополнительного

// перераспределения памяти. Если n меньше текущей емкости, то емкость

// не изменяется. Вызов метода не влияет на размер вектора

void reserve (size_type n);

};

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

class Histogram

{

private:

vector<int> count;

public:

Histogram(int h) :count( max(h,8) ) {}

void record (int i);

//……

};

void Histogram :: record(int i)

{

if (i < 0)

i = 0;

//переопределяем размер вектора

if (count.size() <= i)

count.resize(i+i);

count[i]++;

}

Используя метод reserve() можно резервировать память тем самым, увеличивая емкость вектора и предотвращая нежелательные операции перераспределения памяти. Текущую емкость вектора можно получить с помощью методы capacity(). В приведенном ниже примере, происходит только одно перераспределение памяти при вызове reserve().

vector<int> v;

v.reserve(1000);

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

v.push_back(i);

Кроме улучшения производительности, предотвращение перераспределения памяти с помощью метода reserve() может (должно) быть использовано в случае необходимости сохранить «ссылку» на элемент вектора, например указатель или итератор. Так если получить указатель на какой-либо элемент вектор, после чего добавить большое число элементов в вектор, что приведет к перераспределению памяти и копированию элементов в другую область памяти, полеченный указатель будет недействительным, будет указывать на некую область памяти, которая может содержать что угодно.

vector<int> v(1);

// получаем указатель на первый элемент

int* intPtr = &v[0];

// добавляем большое количество элементов (происходит перераспределение)

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

v.push_back(i);

// пытаемся прочитать первый элемент через указатель, что приводи к

// неопределенному поведению

int i = *intPtr;

Ниже приведен корректный вариант кода:

vector<int> v(1);

v.reserve(1001);

int* intPtr = &v[0];

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

v.push_back(i);

int i = *intPtr;

Использование индексов в качестве ссылок на элементы массива не требует резервирования памяти для предотвращения перемещения элементов, индекса действительны и после перераспределения памяти.

Также следует помнить, что ссылки становятся недействительными после операций приводящих к копированию элементов таких как вставка и удаление элементов в середину вектора.

Еще одной особенностью вектора является то, что уменьшение размера вектора не уменьшает его емкость, просто остается больше места для увеличения вектора в будущем. Более того, уменьшение емкости вектора не происходит никогда, за исключение уничтожения вектора и освобождения всей памяти. Это может привести к чрезмерному переиспользованию памяти. Для уменьшения емкости вектора может быть использован следующий трюк.

vector<int> X;

…// код выполнение которого привело в тому, что Х имеет меленький размер

// и большую емкость

vector<int>(X).swap(X);

Метод swap() имеет следующее определение:

template <class T, class A=allocator<T> > class vector

{

public:

//…..

void swap (vector&);

};

и осуществляет эффективный обмен содержимым двух векторов без копирования элементов.

В приведенном примере на основе вектора X с помощью конструктора копирования создается временный объект (vector<int>(X) ), при этом временный вектор содержит те же элементы что и вектор Х, но имеет меньшую емкость (скорее всего немного большую чем его размер). Далее происходит обмен содержимым временного вектора и вектора Х, после чего избыточную емкость имеет уже временный вектор. Далее, так как используется временный вектор, временный вектор уничтожается деструктором и занимаемая им память освобождается.

Соседние файлы в папке lab1-vector