// инициализировать и возвратить пару, созданную из данных,

 // возвращенных функцией uninitialized_copy()

 return {data, uninitialized_copy(b, e, data)};

}

Функция alloc_n_copy() вычисляет объем резервируемого пространства, вычитая указатель на первый элемент из указателя на следующий после последнего. Зарезервировав память, функция создает в ней копии заданных элементов.

Копирование осуществляется в операторе return при списочной инициализации возвращаемого значения (см. раздел 6.3.2). Указатель-член first возвращенной пары указывает на начало зарезервированной памяти; значение для указателя-члена second возвращается функцией uninitialized_copy() (см. раздел 12.2.2). Это значение будет указателем на следующий элемент после последнего созданного элемента.

Функция-член free()

У функции-члена free() две обязанности: она должна удалить элементы, а затем освободить пространство, зарезервированное объектом класса StrVec. Цикл for вызывает функцию destroy() класса allocator, перебирая элементы в обратном порядке, начиная с последнего существующего элемента и заканчивая первым:

void StrVec::free() {

 // нельзя освободить 0 указателей;

 // если элемент нулевой - не делать ничего

 if (elements) {

  // удалить прежние элементы в обратном порядке

  for (auto p = first_free; p != elements; /* пусто */)

   alloc.destroy(--p);

  alloc.deallocate(elements, cap - elements);

 }

}

Функция destroy() запускает деструктор класса string. Деструктор класса string освобождает память, занятую самой строкой.

Как только элементы будут удалены, освобождается пространство, зарезервированное классом StrVec при вызове функции deallocate(). Указатель, передаваемый функции deallocate(), должен быть именно тем, который ранее создал вызов функции allocate(). Поэтому перед вызовом функции deallocate() сначала проверяется, тот ли это elements, а не нулевой.

Функции-члены управления копированием

При наличии функций-членов alloc_n_copy() и free() функции-члены управления копированием нашего класса очень просты.

StrVec::StrVec(const StrVec &s) {

 // вызов функции alloc_n_copy() для резервирования количества

 // элементов как в s

 auto newdata = alloc_n_copy(s.begin(), s.end());

 elements = newdata.first;

 first_free = cap = newdata.second;

}

Конструктор копий вызывает функцию alloc_n_copy(), а затем присваивает результат вызова переменным-членам. Возвращаемое значение функции alloc_n_copy() является парой указателей. Первый указатель указывает на первый созданный элемент, а второй — на следующий после последнего созданного. Поскольку функция alloc_n_copy() резервирует пространство для точно такого количества элементов, которое было задано, указатель cap также указывает только на следующий после последнего созданного.

Деструктор вызывает функцию free():

StrVec::~StrVec() { free(); }

Оператор присвоения копии вызывает функцию alloc_n_copy() прежде, чем освободить существующие элементы. Это защищает от копирования в себя самого:

StrVec &StrVec::operator=(const StrVec &rhs) {

 // вызов alloc_n_copy() для резервирования точно такого количества

 // элементов, как в rhs

 auto data = alloc_n_copy(rhs.begin(), rhs.end());

 free();

 elements = data.first;

 first_free = cap = data.second;

 return *this;

}

Подобно конструктору копий, оператор присвоения копии использует значения, возвращенные функцией alloc_n_copy(), для инициализации своих указателей.

Перемещение, а не копирование элементов при резервировании

Прежде чем приступить к функции reallocate(), следует обдумать то, что она должна делать:

• зарезервировать память для нового, большего массива строк;

• заполнить первую часть этого пространства существующими элементами;

• удалить элементы в существующей памяти и освободить ее.

Перейти на страницу:

Похожие книги