Следует заметить, что вставка элемента в набор может привести к передаче исключения, поскольку добавление элемента на контейнер требует резервирования памяти, вполне может быть передано исключение bad_alloc (см. раздел 12.1.2). Таким образом, в отличие от функций перемещения классов HasPtr и StrVec, конструктор перемещения и операторы присваивания при перемещении класса Message могли бы передать исключения, поэтому не будем отмечать их как noexcept (см. раздел 13.6.2).

Функция заканчивается вызовом функции clear() объекта m.folders. Известно, что после перемещения объект m.folders вполне допустим, но его содержимое непредсказуемо. Поскольку деструктор класса Message перебирает набор folders, необходимо убедиться, что набор пуст.

Конструктор перемещения класса Message вызывает функцию move(), чтобы переместить содержимое и инициализировать по умолчанию свой член folders:

Message::Message(Message &&m): contents(std::move(m.contents)) {

 move_Folders(&m); // переместить folders и обновить указатели Folder

}

В теле конструктора происходит вызов функции move_Folders(), чтобы удалить указатели на m и вставить указатели на данное сообщение.

Оператор присваивания при перемещении непосредственно проверяет случай присвоения себя себе:

Messages Message::operator=(Message &&rhs) {

 if (this != &rhs) { // прямая проверка присвоения себя себе

  remove_from_Folders();

  contents = std::move(rhs.contents); // присвоение при перемещении

  move_Folders(&rhs); // сбросить папки, чтобы указывать на это

                      // сообщение

 }

 return *this;

}

Подобно любым операторам присвоения, оператор присваивания при перемещении должен удалить прежние данные левого операнда. В данном случае удаление левого операнда требует удаления указателей на это сообщение из существующих папок, что и делает вызов функции remove_from_Folders(). После удаления из папок происходит вызов функции move(), чтобы переместить contents из объекта rhs в this. Остается только вызвать функцию move_Folders(), чтобы модифицировать указатели Folder.

Итераторы перемещения

Функция reallocate() класса StrVec (см. раздел 13.5) использовала вызов функции construct() в цикле for для копирования элементов из прежней памяти в новую. Альтернативой циклу был бы просто вызов функции uninitialized_copy() для создания нового пространства в памяти. Однако функция uninitialized_copy() делает именно то, о чем говорит ее имя: она копирует элементы. Нет никакой аналогичной библиотечной функции для перемещения объектов в пустую память.

Вместо нее новая библиотека определяет адаптер итератора перемещения (move iterator) (см. раздел 10.4). Итератор перемещения адаптирует переданный ему итератор, изменяя поведение его оператора обращения к значению. Обычно оператор обращения к значению итератора возвращает ссылку на l-значение элемента. В отличие от других итераторов, оператор обращения к значению итератора перемещения возвращает ссылку на r-значение.

Обычный итератор преобразуется в итератор перемещения при вызове библиотечной функции make_move_iterator(), которая получает итератор и возвращает итератор перемещения.

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

void StrVec::reallocate() {

 // зарезервировать вдвое больше пространства, чем для текущего

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

 auto newcapacity = size() ? 2 * size() : 1;

 auto first = alloc.allocate(newcapacity);

 // переместить элементы

 auto last = uninitialized_copy(make_move_iterator(begin()),

                                make_move_iterator(end()),

                                first);

 free();           // освободить прежнее пространство

 elements = first; // обновить указатели

 first_free = last;

 cap = elements + newcapacity;

}

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

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