在 unique_ptr 中显式调用对象的复制构造函数

Explicitly calling the copy constructor of an object inside unique_ptr

我正在使用带有 const std::unique_ptr 的 pimpl 惯用法来保存 class 实现。我的 class 需要支持复制构造和复制赋值。我想做的是在 unique_ptr 中手动调用 impl class 的复制构造函数。但是,我看不出如何。

#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.
}

我已经检查了另一个关于在对象上显式调用复制构造函数的问题。一个答案推荐使用 placement new。

Object dstObject;
new(&dstObject) Object(&anotherObject);

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

What I'd like to do is manually call the copy constructor of the impl class inside the unique_ptr

这是你的错误。当您在 potato 的(复制)构造函数中时,没有已经构建的 impl 对象,您必须 "manually" 调用复制构造函数。

只需构建新的 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 运算符在未初始化的存储上显式调用构造函数。您可以 return 通过显式调用其析构函数将对象存储到未初始化状态。

这是赋值运算符的简单实现,它显式调用您已经定义为接口的一部分的复制构造函数和析构函数:

#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 的重载。如果您的 class 支持,您可以使用 swap 习语,或者使用与上面相同的代码,但调用 new(this) potato(std::move(src));.

如果您有权访问包裹在智能指针中的 class 的析构函数和复制构造函数,您可以对它们执行相同的操作,只是取消对智能指针的引用。不过,您可能不想这样做。

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