明确调用unique_ptr中对象的副本构造函数

时间:2018-12-07 21:37:32

标签: c++ c++11 copy-constructor unique-ptr pimpl-idiom

我正在使用带有const std::unique_ptr的pimpl惯用法来保存类实现。我的班级需要支持副本构建和副本分配。我想做的是手动调用implunique_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);

我可以在复制构造函数中使用它吗?如果是这样,怎么办?我真的不明白那里发生了什么。谢谢。

2 个答案:

答案 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&& srcconst potato& src的重载。如果您的班级支持交换习惯,则可以使用交换惯用语,也可以使用与上面相同的代码,但是调用new(this) potato(std::move(src));

如果您可以访问包装在智能指针中的类的析构函数和复制构造函数,则可以对它们执行相同的技巧,只需取消引用智能指针即可。不过,您可能不想这么做。

如果类的内容是智能指针,STL容器等,则默认的复制构造函数和赋值运算符应该可以正常工作。您可能希望通过编写*p = x*p = *qstd::swap之类的东西来复制智能指针引用的数据,而不是显式调用复制构造函数。