Семантика перемещения (move semantics) в C++ была введена в стандарте C++11 для оптимизации работы с временными объектами, минимизируя количество ненужных копирований и повышая производительность. Основная идея заключается в том, чтобы “переместить” ресурсы (например, память) из одного объекта в другой, вместо их копирования.

Копирование и перемещение

Копирование:

  • Конструктор копирования:

    MyClass(const MyClass& other);
  • Оператор присваивания копированием:

    MyClass& operator=(const MyClass& other);

При копировании создаётся новый объект, и все ресурсы оригинала дублируются. Это может быть дорого с точки зрения производительности, особенно для объектов, которые содержат большие массивы данных или управляют внешними ресурсами (например, файл).

Перемещение:

  • Конструктор перемещения:

    MyClass(MyClass&& other) noexcept;
  • Оператор присваивания перемещением:

    MyClass& operator=(MyClass&& other) noexcept;

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

Пример использования перемещения и копирования:

#include <iostream>
#include <utility> // Для std::move
 
class MyClass {
private:
    int* data;
    size_t size;
 
public:
    // Конструктор по умолчанию
    MyClass(size_t s = 0) : size(s) {
        data = s ? new int[s] : nullptr;
        std::cout << "Default Constructor" << std::endl;
    }
 
    // Конструктор копирования
    MyClass(const MyClass& other) : size(other.size) {
        data = size ? new int[size] : nullptr;
        std::copy(other.data, other.data + size, data);
        std::cout << "Copy Constructor" << std::endl;
    }
 
    // Конструктор перемещения
    MyClass(MyClass&& other) noexcept : data(other.data), size(other.size) {
        other.data = nullptr;
        other.size = 0;
        std::cout << "Move Constructor" << std::endl;
    }
 
    // Оператор присваивания копированием
    MyClass& operator=(const MyClass& other) {
        if (this != &other) {
            delete[] data;
            size = other.size;
            data = size ? new int[size] : nullptr;
            std::copy(other.data, other.data + size, data);
        }
        std::cout << "Copy Assignment" << std::endl;
        return *this;
    }
 
    // Оператор присваивания перемещением
    MyClass& operator=(MyClass&& other) noexcept {
        if (this != &other) {
            delete[] data;
            data = other.data;
            size = other.size;
            other.data = nullptr;
            other.size = 0;
        }
        std::cout << "Move Assignment" << std::endl;
        return *this;
    }
 
    // Деструктор
    ~MyClass() {
        delete[] data;
        std::cout << "Destructor" << std::endl;
    }
};
 
int main() {
    MyClass a(10);          // Вызов конструктора по умолчанию
    MyClass b = std::move(a); // Вызов конструктора перемещения
 
    MyClass c;
    c = std::move(b);       // Вызов оператора присваивания перемещением
 
    return 0;
}

Повышение быстродействия программы за счёт перемещения:

  • Уменьшение накладных расходов: Перемещение ресурсов (например, памяти) значительно дешевле копирования, так как нет необходимости дублировать данные.
  • Эффективное управление памятью: Перемещая ресурсы, мы избегаем ненужных операций выделения и освобождения памяти.
  • Ускорение выполнения программ: За счёт уменьшения количества копирований программа выполняется быстрее, что особенно важно при работе с большими данными или в высокопроизводительных приложениях.

Варианты использования перемещения:

  1. Вектор перемещаемых объектов:

    std::vector<MyClass> vec;
    vec.push_back(MyClass(10)); // Вызов конструктора перемещения
  2. Возврат временного объекта из функции:

    MyClass createMyClass() {
        MyClass temp(10);
        return temp; // Вызов конструктора перемещения
    }
    MyClass obj = createMyClass();