以下程序因错误的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
的实施不符合标准?
对于那些问我为什么要这样做的人:
我目前正在尝试弄清楚如何临时管理由new
和new[]
创建的对象的裸指针。我们的想法是使用std::shared_ptr::reset()
将删除器替换为无操作删除器。另一种方法是使用try { stuff() } catch( ... ) { delete x; throw;}
块来包装代码。
答案 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[]>
。)