Явный вызов конструктора копирования объекта в unique_ptr
Я использую идиому pimpl с const std::unique_ptr
провести реализацию класса. Мой класс должен поддерживать конструкцию копирования и назначение копирования. То, что я хотел бы сделать, это вручную вызвать конструктор копирования impl
класс внутри unique_ptr
, Однако я не вижу, как.
#include <memory>
struct potato {
potato();
~potato();
potato(const potato& p);
private:
struct impl;
const std::unique_ptr<impl> _pimpl;
};
struct potato::impl {
int carbs = 42;
};
potato::potato()
: _pimpl(std::make_unique<impl>()) {
}
potato::~potato() = default;
potato::potato(const potato& p) {
// Try to call the copy constructor of impl, stored in unique_ptr, not the
// unique_ptr copy-constructor (which doesn't exist).
_pimpl.get()->impl(p._pimpl); // This doesn't work.
}
Я проверил еще один вопрос о явном вызове конструктора копирования для объекта. Один ответ рекомендуется с использованием нового размещения.
Object dstObject;
new(&dstObject) Object(&anotherObject);
Могу ли я использовать это в моем конструкторе копирования? Если так, то как? Я не очень понимаю, что там происходит. Спасибо.
2 ответа
То, что я хотел бы сделать, это вручную вызвать конструктор копирования
impl
класс внутриunique_ptr
Здесь кроется ваша ошибка. Поскольку вы находитесь внутри (копирования) конструктора potato
, нет никаких impl
уже созданный объект, над которым вам придется "вручную" вызывать конструктор копирования.
Просто создай свой новый impl
передавая ему ссылку на оригинал impl
копировать.
potato::potato(const potato& p)
: _pimpl(std::make_unique<impl>(*p._pimpl) {
}
Что касается назначения, вы можете легко переслать на impl
оператор присваивания:
potato &operator=(const potato &p) {
*_pimpl = *p._pimpl;
return *this;
}
Вы можете явно вызывать конструкторы в неинициализированном хранилище с размещением new
оператор, как вы упомянули. Вы можете вернуть хранилище объекта в неинициализированное состояние, явно вызвав его деструктор.
Вот простая реализация оператора присваивания, который явно вызывает конструктор копирования и деструктор, который вы уже определили как часть вашего интерфейса:
#include <new>
potato& potato::operator=(const potato& x)
{
if ( this != &x ) { // check for self-assignment!
this->~potato();
new(this) potato(x);
}
return *this;
}
Возможно, вы также захотите определить конструктор перемещения и перегрузить оператор присваивания, когда правая часть является временной. Перегруз для potato&& src
так же как const potato& src
, Вы можете использовать идиому подкачки, если ваш класс поддерживает его, или тот же код, что и выше, но вызов new(this) potato(std::move(src));
,
Если у вас есть доступ к деструктору и конструктору копирования класса, заключенному в умный указатель, вы можете сделать с ними тот же трюк, просто разыменовав умные указатели. Вы, вероятно, не хотите, хотя.
Конструктор копирования по умолчанию и оператор присваивания должны работать нормально, если содержимое класса - это интеллектуальные указатели, контейнеры STL и т. Д. Возможно, вы захотите скопировать данные, на которые ссылаются умные указатели, написав *p = x
или же *p = *q
или же std::swap
, а не явные вызовы конструктора копирования.