为什么 shared_ptr 没有 Deleter 参数
发现对于指针可能拥有的删除器,标准以两种完全不同的方式定义std::unique_ptr
和std::shared_ptr
时,我觉得非常奇怪。cppreference::unique_ptr和cppreference::shared_ptr的声明如下:
template<
class T,
class Deleter = std::default_delete<T>
> class unique\_ptr;
template< class T > class shared\_ptr;
如您所见,unique_ptr
将Deleter-object
的类型“保存”为模板参数。这也可以从后面从指针中检索Deleter
的方式中看到:
// unique\_ptr has a member function to retrieve the Deleter
template<
class T,
class Deleter = std::default_delete<T>
>
Deleter& unique\_ptr<T, Deleter>::get_deleter();
// For shared\_ptr this is not a member function
template<class Deleter, class T>
Deleter* get\_deleter(const std::shared\_ptr<T>& p);
// unique\_ptr has a member function to retrieve the Deleter
template<
class T,
class Deleter = std::default_delete<T>
>
Deleter& unique\_ptr<T, Deleter>::get_deleter();
// For shared\_ptr this is not a member function
template<class Deleter, class T>
Deleter* get\_deleter(const std::shared\_ptr<T>& p);
有人能解释一下这种差异背后的原因吗?我显然喜欢unique_ptr
的概念,为什么这并不适用于shared_ptr
?另外,为什么在后一种情况下get_deleter
是非成员函数?
有人将不得不挖掘原来的提议,但我的猜测是:没有deleter
作为模板参数使shared_ptr
更容易使用,但您需要支付类型擦除成本。使get_deleter
成为成员将使编写接受shared_ptr<T>
的泛型代码变得更加乏味—您需要编写sp.template get_deleter<Deleter>()
而不是get_deleter<Deleter>(sp)
。这就是为什么std::get
是非成员的原因。
Following the "as close as possible" principle, the proposed smart pointers have a single template parameter, the type of the pointee. Avoiding additional parameters ensures interoperability between libraries from different authors, and also makes shared_ptr easier to use, teach and recommend.
也就是说,unique_ptr
的设计目标之一是它的开销应该(非常接近)为零。删除删除者的类型很方便,但会带来擦除带来的运行时开销。
您还应该注意,由于这种差异,即使Base
没有虚拟析构函数,shared_ptr<Base> p = make_shared<Derived>()
也会执行正确的操作。
Since the deleter is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation.
这也很有用,因为它为std::shared_ptr
的客户端提供了更多的灵活性,例如,具有不同删除器的shared_ptr
实例可以存储在同一个容器中。
此外,因为shared_ptr
实现无论如何都需要一个共享内存块(用于存储引用计数),而且与原始指针相比已经有一些开销,所以在这里添加一个type-erased的删除器并不是什么大问题。
另一方面,unique_ptr
没有任何开销,每个实例都必须嵌入它的deleter
,因此将它作为类型的一部分是很自然的事情。
当一个虚函数需要返回智能指针式,且T是复合类型时,建议使用shared_ptr
,应为默认的std::default_delete<T>
可能不支持复合类型的deleter
。
shared_ptr
Memory Footprint
In the Boost implementation, the size of shared_ptr (and weak_ptr)
is twice the size of a raw pointer. More precisely, sizeof(shared_ptr<T>) == sizeof(weak_ptr<T>) == sizeof(T*) + sizeof(C*)
, where C
is a structure holding the reference counts and other information. This "twin pointer" layout is typical for most existing reference counted smart pointers.
The count structure C
has the following members:
- Virtual table pointer (C is polymorphic);
- Use count;
- Weak count;
- Mutex (for multithreaded builds);
- Original pointer passed to the constructor;
- Deleter.
Deleter Constructor
Custom deleters allow a factory function returning a shared_ptr
to insulate the shared_ptr
user from its memory allocation strategy. Since the deleter is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation. For example, a "no-op" deleter is useful when returning a shared_ptr
to a statically allocated object. A deleter that invokes the Release()
member function enables shared_ptr
to hold a pointer to a COM
object, and other variations allow a shared_ptr
to be used as a wrapper for another smart pointer, easing interoperability.
参考文献
- https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html
- https://stackoverflow.com/questions/27742290/deleter-type-in-unique-ptr-vs-shared-ptr