为什么shared_ptr仅使用一个时,unique_ptr会使用两个模板参数?


69

双方unique_ptrshared_ptr接受定制的析构函数他们所拥有的对象上调用。但在的情况下unique_ptr,析构函数作为一个模板参数传递,而类型shared_ptr的自定义析构函数将被指定为一个模板参数的构造函数

template <class T, class D = default_delete<T>> 
class unique_ptr
{
    unique_ptr(T*, D&); //simplified
    ...
};

template<class T>
class shared_ptr
{
    template<typename D>
    shared_ptr(T*, D); //simplified
    ...
};

我不明白为什么会有这样的差异。有什么要求?


10
shared_ptr类型删除器,即的用户shared_ptr不必知道删除器的类型。这具有运行时成本(分配,取消引用),因此不执行unique_ptr(这是无开销的)。例如,请参阅stackoverflow.com/q/6324694/420683
dyp 2014年

2
@dyp好的,但是为什么shared_ptr要这样做呢?
qdii 2014年

1
@Nemo无论如何shared_ptr都必须存储一个簿记对象,因此它已经需要额外的分配(当不使用时make_shared)。同样,有时可以将shared_ptr的引用计数机制与不在簿记对象共享所有权下指针一起使用。
dyp 2014年

4
@qdiishared_ptr表示共享所有权。它并不需要所有所有者知道如何销毁该对象,并且这可能已经足够提供提供这种类型擦除的理由。由于记账对象的原因,开销也不会太大。
dyp 2014年

4
@qdii IIRC的实现shared_ptr几乎没有类型擦除的开销,因为它们只是将其与簿记对象所需的开销结合在一起。-尽管可能会要求任何所有者销毁所有者拥有的东西shared_ptr,但所有者无需知道如何去做,即不需要看到该所有者所拥有的释放功能的定义或声明。事情。
dyp 2014年

Answers:


60

如果您将deleter作为模板参数提供(如所示unique_ptr),则它是该类型的一部分,并且您无需在此类型的对象中存储任何其他内容。如果将deleter作为构造函数的参数传递(如中所述shared_ptr),则需要将其存储在对象中。这是额外灵活性的代价,因为您可以对相同类型的对象使用不同的删除器。

我猜这是原因:unique_ptr应该是开销非常轻的对象,零开销。每个存储删除器unique_ptr的大小可能增加一倍。因此,人们会改用旧的原始指针,这是错误的。

另一方面,shared_ptr它不是轻量级的,因为它需要存储引用计数,因此存储自定义删除器看起来也很不错。


1
“您可以对相同类型的对象使用不同的删除器”:您是否表示标准希望允许以不同的方式销毁shared_ptr指向同一对象的两个不同的指针?
qdii 2014年

3
@qdii-否,不是同一对象,而是同一类型的不同shared_ptr。
Wojtek Surowka,2014年

1
@WojtekSurowka等待,如果它们指向相同类型T的不同对象,则将析构函数作为类参数传递就足以让用户为它们中的每个调用不同的析构函数。仅std::shared_ptr<T, D1>用于第一个,std::shared_ptr<T, D2>第二个。
qdii 2014年

2
@qdii:您需要想象一个带有shared_ptras参数的函数,或者一个存储shared_ptras成员变量的对象。不必关心删除器,使它们更加灵活。
Nemo 2014年

2
这都是关于权衡的。unique_ptr不应有任何开销,因此您描述的复杂性是必需的成本。在shared_ptr的情况下,已经存在一些开销,因此可以采用更灵活的方式进行设计。
Wojtek Surowka 2014年

2

不同类型的共享指针可以共享同一对象的所有权。看到过载(8)std::shared_ptr::shared_ptr。唯一指针不需要这种机制,因为它们不需要共享

template< class Y > 
shared_ptr( const shared_ptr<Y>& r, element_type* ptr ) noexcept;

如果您不键入删除工具的类型,则将无法使用,例如shared_ptr<T, Y_Deleter>a shared_ptr<T>,这将使其基本上变得无用。

你为什么要这么重载?

考虑

struct Member {};
struct Container { Member member };

如果您想保持Container生命,可以在使用的同时Member进行

std::shared_ptr<Container> pContainer = /* something */
std::shared_ptr<Member> pMember(pContainer, &pContainer->member);

并且只需保留pMember(也许放在std::vector<std::shared_ptr<Member>>

或者,使用重载(9)

template< class Y > 
shared_ptr( const shared_ptr<Y>& r ) noexcept; 
  // Only exists if Y* is implicitly convertible to T*

您可以进行多态共享

struct Base {};
struct Derived : Base {};

void operate_on_base(std::shared_ptr<Base>);

std::shared_ptr<Derived> pDerived = /* something*/
operate_on_base(pDerived);
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.