我正在使用带有const std::unique_ptr
的pimpl惯用法来保存类实现。我的班级需要支持副本构建和副本分配。我想做的是手动调用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.
}
我已经解决了另一个关于在对象上显式调用copy-constructor的问题。建议使用新的刊登位置。
Object dstObject;
new(&dstObject) Object(&anotherObject);
我可以在复制构造函数中使用它吗?如果是这样,怎么办?我真的不明白那里发生了什么。谢谢。
答案 0 :(得分:5)
我想做的是在
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;
}
答案 1 :(得分:0)
您可以使用放置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
之类的东西来复制智能指针引用的数据,而不是显式调用复制构造函数。