我使用带有const std::unique_ptr
的pimpl习惯用法来保存类实现。我的班级需要支持复印构造和复印作业。我想做的是手动调用unique_ptr
中impl
类的复制构造函数。然而,我不知道该怎么做。
#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);
我可以在复制构造函数中使用它吗?如果是,如何?我真的不明白那里发生了什么。非常感谢。
我想做的是手动调用
unique_ptr
中impl
类的复制构造函数
您的错误就在这里。由于您在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;
}
您可以使用placementnew
操作符在未初始化的存储上显式调用构造函数,正如您所提到的。通过显式调用对象的析构函数,可以将对象的存储返回到未初始化状态。
下面是赋值运算符的一个简单实现,它显式调用复制构造函数和您已经定义为接口一部分的析构函数:
#include <new>
potato& potato::operator=(const potato& x)
{
if ( this != &x ) { // check for self-assignment!
this->~potato();
new(this) potato(x);
}
return *this;
}
您可能还想定义一个move构造函数,并在右侧是临时运算符时重载赋值运算符。即potato&& src
和const potato& src
的过载。如果您的类支持交换习惯用法,您可以使用它,或者使用与上面相同的代码,但调用new(this) potato(std::move(src));
。
如果您可以访问封装在智能指针中的类的析构函数和复制构造函数,则可以对它们执行相同的操作,只需取消对智能指针的引用。不过你可能不想。
如果类的内容是智能指针、STL容器等,默认的复制构造函数和赋值运算符应该可以正常工作。您可能希望通过编写*p = x
、*p = *q
或std::swap
之类的内容来复制智能指针引用的数据,而不是显式调用复制构造函数。