如果我将std :: shared_ptr重置为自身会发生什么

时间:2012-03-20 11:12:21

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

以下程序因错误的glibc双重自由错误而崩溃:

#include <iostream>
#include <memory>

class foo {
public:
   foo()
   {
      std::cout << "foo constructed" << std::endl;
   }

   ~foo()
   {
      std::cout << "foo destructed" << std::endl;
   }
};

int main() {
   auto f = std::make_shared< foo >();
   std::cout << "Before reset" << std::endl;
   f.reset( f.get() );
   std::cout << "After reset" << std::endl;
   return 0;
}

从此我得到以下输出(后跟glibc错误):

foo constructed
Before reset
foo destructed
After reset
foo destructed

很明显,在这种情况下,对象会被销毁两次。一旦重置,一次std::shared_ptr超出范围。这实际上就是我所期望的。

cppreference上,我找到以下文字(在http://en.cppreference.com/w/cpp/memory/shared_ptr/reset找到):

  

如果*这已经拥有一个对象并且它是拥有它的最后一个shared_ptr,那么该对象将通过拥有的删除器销毁,除非ptr是指向它的指针。

在我看来,这实际上说,对象不应该像我的例子那样被销毁。相当令人惊讶,但如果标准这样说。我是以某种方式误读了这个,还是我std::shared_ptr的实施不符合标准?

对于那些问我为什么要这样做的人:

我目前正在尝试弄清楚如何临时管理由newnew[]创建的对象的裸指针。我们的想法是使用std::shared_ptr::reset()将删除器替换为无操作删除器。另一种方法是使用try { stuff() } catch( ... ) { delete x; throw;}块来包装代码。

1 个答案:

答案 0 :(得分:8)

reset的重载规范在20.7.2.2.4 shared_ptr修饰符[util.smartptr.shared.mod],第3段(来自n3290)中给出:

template<class Y> void reset(Y* p);
     

效果:相当于shared_ptr(p).swap(*this)

正如您所看到的那样,shared_ptr(p)构造为p创建了一个带有新删除器的新计数,因此没有什么好处可以解决它。最好将std::shared_ptr<T>::get视为严格意义上的观察者,并用它来处理终生管理是一个明显的迹象,表明存在错误。

另一方面,std::unique_ptr<T>release,这正是您需要介入并立即处理所有权时所需要的。也许您可以更改设计以使用std::unique_ptr<T>?如果需要,最终可以从中创建std::shared_ptr<T>。 (虽然当std::shared_ptr<T>std::unique_ptr<T>中选择删除者时,您仍然需要在数组中进行特殊处理,因为您可能需要std::shared_ptr<T*>中的std::unique_ptr<T[]>。)