Зачем нужен конструктор перемещения, если есть оператор перемещения(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), то:
недостающие операции копирования будут определены автоматически с
поведением по умолчанию
недостающие операции перемещения определены не будут.
Это следует учитывать при написании классов.
Комментариев нет:
Отправить комментарий