shared_ptr 与 unique_ptr 的 deleter

火山方舟向量数据库大模型

为什么 shared_ptr 没有 Deleter 参数

发现对于指针可能拥有的删除器,标准以两种完全不同的方式定义std::unique_ptrstd::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_ptrDeleter-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.

参考文献

picture.image

0
0
0
0
关于作者

文章

0

获赞

0

收藏

0

相关资源
云原生可观测性技术的落地实践
云原生技术和理念在近几年成为了备受关注的话题。应用通过云原生改造,变得更动态、弹性,可以更好地利用云的弹性能力。但是动态、弹性的环境也给应用以及基础设施的观测带来了更大的挑战。本次分享主要介绍了云原生社区中可观测性相关的技术和工具,以及如何使用这些工具来完成对云原生环境的观测。
相关产品
评论
未登录
看完啦,登录分享一下感受吧~
暂无评论