Страницы

Поиск по вопросам

среда, 17 октября 2018 г.

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

Зачем нужен конструктор перемещения, если есть оператор перемещения(operator=(T&&))?


Ответ

Конструктор и оператор перемещения используются компилятором в разных ситуациях:
конструктор перемещения применяется в местах, где объявление совпадает с определением (инициализацией) rvalue-ссылкой на экземпляр этого же класса, либо посредством direct initialization в конструкторе класса/структуры (если же определение произойдет с помощью lvalue-ссылки, то вызовется конструктор копирования) оператор перемещения применяется в местах, где экземпляр класса уже был ранее определен и к нему применяется operator =, который в качестве аргумента приминает rvalue-ссылку на экземпляр этого же класса (если же оператор принимает lvalue-ссылку , то вызовется оператор присваивания)
Про rvalue-ссылки можете почитать здесь, здесь и здесь

Контрольный пример, для разъяснения отличия в работе данных конструкций
#include
class Buffer { public: Buffer(const std::string& buff) : pBuff(nullptr) , buffSize(buff.length()) { pBuff = new char[buffSize]; memcpy(pBuff, buff.c_str(), buffSize); }
~Buffer(){ destroy(); }
Buffer(const Buffer& other) : pBuff(nullptr) , buffSize(other.buffSize) { pBuff = new char[buffSize]; memcpy(pBuff, other.pBuff, buffSize); }
Buffer& operator=(const Buffer& other) { destroy(); buffSize = other.buffSize; pBuff = new char[buffSize]; memcpy(pBuff, other.pBuff, buffSize); return *this; }
Buffer(Buffer&& tmp) : pBuff(tmp.pBuff) , buffSize(tmp.buffSize) { tmp.pBuff = nullptr; }
Buffer& operator=(Buffer&& tmp) { destroy(); buffSize = tmp.buffSize; pBuff = tmp.pBuff; tmp.pBuff = nullptr; return *this; }
private: void destroy() { if (pBuff) delete[] pBuff; }
char* pBuff; size_t buffSize; };
Buffer CreateBuffer(const std::string& buff) { Buffer retBuff(buff); return retBuff; }
int main() { Buffer buffer1 = CreateBuffer("123"); // срабатывает конструктор перемещения Buffer buffer2 = buffer1; // срабатывает конструктор копирования buffer2 = CreateBuffer("123"); // срабатывает конструктор перемещения, затем оператор перемещения buffer2 = buffer1; // срабатывает оператор присваивания }

Небольшое дополненеие В C++11 каждый класс, помимо конструктора по умолчанию, имеет следующие 5 дефолтных операций:
Конструктор копирования (copy constructor) Оператор присваивания (copy assignment) Конструктор перемещения (move constructor) Оператор перемещения (move assignment) Деструктор (destructor)
При определении одной из этих 5-ти операций рекомендуется явно указать (либо определить, либо объявить с помощью default или delete) все остальные, т.к. все эти 5 операций тесно связаны. Это будет способствовать лучшему пониманию семантики класса при чтении кода. Если явно определена одна из упомянутых 5-ти операций (в том числе с использованием default или delete), то:
недостающие операции копирования будут определены автоматически с поведением по умолчанию недостающие операции перемещения определены не будут.
Это следует учитывать при написании классов.

Комментариев нет:

Отправить комментарий