在null指针的情况下,删除器的标准行为在shared_ptr和unique_ptr之间是否不同?

时间:2012-06-22 21:26:49

标签: c++ c++11 shared-ptr unique-ptr nullptr

好的,首先是一些可能相关的事情:

我在C ++ 11模式下使用Clang 3.1编译器,标准库设置为libc ++。

我正在尝试熟悉C ++ 11,并且这样做我遇到的行为似乎很奇怪。它可能是Clang或libc ++的怪癖,但我不能说C ++标准,我无法访问其他支持C ++ 11的编译器,所以我无法检查它,我搜索了互联网和Stack Overflow在没有找到任何相关内容的情况下尽我所能......所以我们走了:

当使用shared_ptr / unique_ptr为简单资源实现RAII时,它们的行为似乎与删除时的空指针不同。我意识到通常没有必要删除空指针,但我原本期望这个行为至少在两个STL智能指针之间匹配。

针对具体情况,请考虑以下代码:

{
    auto Deleter = [](void *){cout << "It's later!" << endl;};
    shared_ptr<void> spDoSomethingLater(nullptr, Deleter);
    unique_ptr<void, void (*)(void *)> upDoSomethingLater(nullptr, Deleter);
    cout << "It's now!" << endl;
}

我原本期望得到以下输出之一:

a)如果两个删除器都被调用,即使指针为空:

"It's now!"
"It's later!"
"It's later!"

b)如果由于指针为空而未调用任何删除函数:

"It's now!"

但我没有观察到这些情况。相反,我观察到:

"It's now!"
"It's later!"

这意味着正在调用一个但不是另一个删除者。经过进一步研究,我发现shared_ptr的删除器被调用,无论它是否包含空值,但只有在没有空值的情况下才会调用unique_ptr的删除器。

我的问题: 这实际上是标准规定的正确行为吗?如果是这样,为什么两种STL类型之间的指定行为会以这种方式不同?如果没有,这是一个我应该向libc ++报告的错误吗?

2 个答案:

答案 0 :(得分:28)

观察到的行为符合标准。

对于unique_ptr,20.7.1.2.2 / 2(析构函数效果)说

  

效果:如果get() == nullptr没有效果。除此以外   get_deleter()(get())

对于shared_ptr,20.7.2.2.2 / 1表示即使包装空指针也应该调用删除器:

  

效果:

     
      
  • 如果*这是空的或与另一个共享所有权   shared_ptr实例(use_count() > 1),没有副作用。
  •   
  • 否则,如果*此拥有对象p和删除者d,则会调用d(p)
  •   
  • 否则,*它拥有一个指针p,并调用delete p
  •   

这里重要的细节是“拥有一个对象p”。 20.7.2.2/1表示“shared_ptr对象为空如果它不拥有指针”。 20.7.2.2.1 / 9(相关构造函数)说它“构造了一个拥有对象shared_ptr和删除者p”的d对象。

据我所知,该调用在技术上使shared_ptr 拥有空指针,从而导致调用删除器。将此与无参数构造函数进行对比,该构造函数被称为shared_ptr”。

答案 1 :(得分:10)

是的,这是正确的行为。

§20.7.1.2.2[unique.ptr.single.dtor] / 2:

  

unique_ptr析构函数

     

效果:如果get() == nullptr没有效果。否则get_deleter()(get())

§20.7.2.2.2[util.smartptr.shared.dest] / 1:

  

shared_ptr析构函数

     

效果

     
      
  • 如果*this 为空或与另一个shared_ptr实例(use_count() > 1)共享所有权,则无副作用。
  •   
  • 否则,如果*this 拥有对象p和删除者d,则会调用d(p)
  •   
  • 否则,*this 拥有指针p,并调用删除p
  •   

因为shared_ptr是空的,所以应该没有效果?错了,因为提供a指针会使shared_ptr 为空,即使指针为空。

§20.7.2.2.1[util.smartptr.shared.const] / 8-10

  

shared_ptr构造函数

template<class Y, class D> shared_ptr(Y* p, D d);
template<class Y, class D, class A> shared_ptr(Y* p, D d, A a);
template <class D> shared_ptr(nullptr_t p, D d);
template <class D, class A> shared_ptr(nullptr_t p, D d, A a);
     

要求: p可转换为T*。 ...

     

效果:构造拥有对象shared_ptr和删除者p的{​​{1}}对象。

     

后置条件: d

这意味着shared_ptr 拥有 nullptr。因此,当shared_ptr被销毁时,将调用删除器。