2

根据下表,当您将 aunique_ptr<T>用于前向声明的类型T时,unique_ptr析构函数需要T是完整的,但移动赋值运算符(和reset)也需要:

https://stackoverflow.com/a/6089065/1794803

因此,对于您的pImpl习惯用法,要正确实现它,您必须声明deleteand move assignment method(作为副作用,将它们标记为非内联):

class impl_t;

class A
{
   std::unique_ptr<impl_t> p_impl;

public:
   // Implement in A.cpp as A::~A() = default;
   ~A();

   // Implemented in A.cpp as A& operator=(A&&) = default;
   A& operator=(A&& he);
};

但是,既然std::unique_ptr是动态内存的 RAII 解决方案,并且您pImpl已经在一个类中,并且无论如何您都被迫编写一个析构函数,那么只管理一个原始指针不是更好吗,因为您的类已经是一个 RAII-就像从?的角度来看p_impl

class impl_t;

class A
{
    impl_t* p_impl;

public:
    ~A(); // The destructor must be written anyway.

    // The omitted move assignment destructor doesn't cause UB.
};

这不是更好的解决方案吗?(+ 定义或删除您自己的复制/移动运算符,如果您想分类是否可复制/可移动;但这是一个“有意识的选择”;但是,不写移动分配unique_ptr是错误的)。

使用 a unique_ptronly 可以节省您在析构函数中编写 a delete p_impl,无论如何您都必须声明。

unique_ptr对于即使在异常情况下也会被破坏的局部动态对象是一个很好的选择,但是对于“属性”,如果你不记得你必须重写移动赋值运算符,你除了获得 UB 的可能性之外什么也没有保存。

4

2 回答 2

4

好吧,使用 a可以使std::unique_ptr您免于deletep_impl.

此外,它应该在构造函数中的并发访问和异常情况下运行良好(使用原始指针和您自己似乎无法保证new)。

于 2016-12-10T17:42:03.003 回答
0

std::unique_ptr 应该是 pimpl 的首选方式。作为参考,请参阅Herb Sutter 在 CppCon16上大约 10 分钟的演讲。原因是,它将防止您在维护 RAII 时意外更改您的 pimpl。

于 2016-12-10T18:13:09.913 回答