Перейти к содержимому

Перемещение

Введение

Перемещение в C++ — это оптимизированная форма передачи ресурсов, позволяющая эффективно передавать владение ресурсами между объектами без необходимости их копирования. Эта техника была введена в стандарт C++11 для улучшения производительности и управления ресурсами.

Семантика перемещения

Зачем нужна семантика перемещения?

Когда объект передается в функцию или возвращается из функции, копирование этого объекта может быть дорогим. Семантика перемещения позволяет передавать ресурсы без копирования, что делает операции более эффективными.

Перемещающий конструктор и перемещающий оператор присваивания

Перемещающий конструктор

Перемещающий конструктор создаёт новый объект, используя ресурсы другого объекта, переданного по rvalue-ссылке.

Перемещающий оператор присваивания

Перемещающий оператор присваивания переносит ресурсы из одного объекта в другой, освобождая ресурсы, которыми владеет целевой объект.

Примеры

Пример класса с семантикой перемещения

#include <iostream>
#include <utility>
class Resource {
public:
Resource() : data(new int[1000]) {
std::cout << "Resource acquired" << std::endl;
}
~Resource() {
delete[] data;
std::cout << "Resource destroyed" << std::endl;
}
// Перемещающий конструктор
Resource(Resource&& other) noexcept : data(other.data) {
other.data = nullptr;
std::cout << "Resource moved" << std::endl;
}
// Перемещающий оператор присваивания
Resource& operator=(Resource&& other) noexcept {
if (this != &other) {
delete[] data; // Удаляем старые данные
data = other.data; // Перемещаем указатель
other.data = nullptr; // Обнуляем указатель у другого объекта
std::cout << "Resource move-assigned" << std::endl;
}
return *this;
}
private:
int* data;
};
int main() {
Resource res1;
Resource res2 = std::move(res1); // Использование перемещающего конструктора
Resource res3;
res3 = std::move(res2); // Использование перемещающего оператора присваивания
return 0;
}

Вывод

Resource acquired
Resource moved
Resource acquired
Resource move-assigned
Resource destroyed
Resource destroyed

Подробности семантики перемещения

std::move

std::move — это функция, которая выполняет явное преобразование объекта в rvalue-ссылку, что позволяет использовать перемещающий конструктор или перемещающий оператор присваивания.

noexcept

Перемещающие операции должны быть помечены спецификатором noexcept, чтобы гарантировать отсутствие исключений во время перемещения. Это также позволяет компилятору оптимизировать код.

std::swap

Для поддержки эффективного перемещения рекомендуется реализовать std::swap, который использует перемещающие операции.

Пример реализации std::swap

#include <utility>
void swap(Resource& a, Resource& b) noexcept {
using std::swap;
swap(a.data, b.data);
}

Паттерны и идиомы

Move-only типы

Типы, которые можно только перемещать, но нельзя копировать. Они полезны для классов, управляющих ресурсами, такими как уникальные указатели (std::unique_ptr).

Perfect Forwarding

Идеальное перенаправление используется в шаблонных функциях для переноса аргументов как копируемых, так и перемещаемых.

Пример идеального перенаправления

#include <utility>
template <typename T>
void wrapper(T&& arg) {
function(std::forward<T>(arg));
}
void function(int& x) {
std::cout << "Lvalue" << std::endl;
}
void function(int&& x) {
std::cout << "Rvalue" << std::endl;
}
int main() {
int a = 10;
wrapper(a); // Вызов lvalue версии
wrapper(10); // Вызов rvalue версии
return 0;
}

Вывод

Lvalue
Rvalue

Резюме

  • Перемещение в C++ позволяет передавать владение ресурсами без копирования.
  • Семантика перемещения включает в себя перемещающий конструктор и перемещающий оператор присваивания.
  • std::move используется для явного указания на перемещение.
  • noexcept улучшает производительность и надёжность перемещающих операций.
  • Примеры: реализация перемещающего конструктора, перемещающего оператора присваивания, std::swap, идеального перенаправления.