C++/pimpl:原始指针还是unique_ptr?什么是更好的选择?
C++/pimpl: raw pointer or unique_ptr? What is a better choice?
当您将unique_ptr<T>
用于前向声明的类型T
时,unique_ptr
析构函数要求T
是完整的,但移动赋值运算符也(和reset
),根据下表:
https://stackoverflow.com/a/6089065/1794803
因此,对于您的pImpl
习语,要正确实现它,您必须声明delete
和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
已经在类中,并且无论如何您都被迫编写析构函数,因此只管理原始指针不是更好吗,因为从p_impl
的角度来看,您的类已经是类似 RAII 的?
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.
};
这不是更好的解决方案吗?(+ 定义或删除你自己的 copy/move 运算符,如果你想类是可复制的/可移动的;但这是一个"有意识的选择";但是,不要为unique_ptr
写移动作业是一个错误)。
使用unique_ptr
只能节省您在析构函数中编写delete p_impl
,无论如何都必须声明该析构函数。
unique_ptr
是本地动态对象的绝佳选择,即使在异常情况下也会被破坏,但对于"属性",如果您不记得必须重写移动赋值运算符,您只能保存获得 UB 的可能性。
好吧,使用std::unique_ptr
可以使您免于为p_impl
的显式delete
而烦恼。
此外,在并发访问和构造函数中的异常情况下,它应该可以很好地工作(使用原始指针和自己new
似乎不能保证)。
std::unique_ptr 应该是 pimpl 根据的首选方式。有关参考,请参阅Herb Sutter在CppCon16上约10分钟的演讲。 原因是,它可以防止您在维护 RAII 时意外更换疙瘩。
在问题中,不需要定义移动分配运算符是期望的优势。下面我建议另一个解决方案,"也"具有这个优势,而且你不需要定义析构函数。我仍然认为以下解决方案最重要的优点是现在可以在匿名命名空间(或未命名命名空间)中定义实现。 您必须付出的代价是(可重用)基类的声明:impl_t
。
#include <iostream>
#include <memory>
////////////////////////
// a utility impl.h file
struct impl_t
{
virtual ~impl_t() = 0;
};
inline impl_t::~impl_t() {}
///////////////////////
// the a_class.cpp file
class a_class_t
{
std::unique_ptr<impl_t> m_pimpl;
public:
a_class_t();
int a_method(int);
};
///////////////////////
// the a_class.cpp file
namespace { // anonymous
struct a_class_impl_t
: impl_t
{
int a_method(int x)
{
return x + 1;
}
~a_class_impl_t()
{
std::cout << "~a_class_impl_t()n";
}
};
} // anonymous namespace
int a_class_t::a_method(int x)
{
return dynamic_cast<a_class_impl_t&>(*m_pimpl).a_method(x);
}
a_class_t::a_class_t()
: m_pimpl(std::make_unique<a_class_impl_t>())
{}
////////////////////
// the main.cpp file
int main()
{
a_class_t a_class;
std::cout << a_class.a_method(1) << 'n';
}
而且这个解决方案最好有一个unique_ptr
.
- 对于没有功能的结构,"new (ptr) mystruct;"做什么?
- "[ptr, ptr+len) must be a valid range"是什么意思?
- 这个陈述'p = I.ptr<uchar>(i);有什么作用?
- 这是什么意思 *ptr = len
- 删除PTR C 后会发生什么
- 在C++中,对象什么时候真正被销毁?delete(ptr)做什么
- C++中的大小释放:全局运算符delete的正确行为是什么(void*ptr,std::size_t size)
- 这个声明"int(*ptr[3])();"是什么意思?
- (PTR + n-1) & -n 这个表达式有什么作用?
- (ptr + i)->Func() 和 &ptr[i] 之间有什么区别吗?Func()
- 如果您使一个char*ptr存储整数变量的地址,会发生什么
- 当使用运算符 new 更改为其他内存的指向已分配内存的 ptr 时会发生什么
- *ptr 和 * ptr 有什么区别
- (ptr语言 - A[0]) / (sizeof(A[0]) / sizeof(A[0][0]))的类型是什么?< /
- 这个语句表示什么[func ptr]
- 使用auto_ptr时,*ptr 和 *ptr.get() 有什么区别
- 在链表中设置head等于Node* ptr意味着什么?
- "(void)!ptr"做什么?
- 'Bad Ptr'在视觉C++中是什么意思?
- 这"int logprintf( const char* ptr, ... )"做什么?