std::shared_ptr 在空指针上调用非默认删除程序
std::shared_ptr calls non-default deleter on null pointer
参见这个例子:
#include <iostream>
#include <memory>
class Foo {
public:
Foo() { std::cout << "Foo()n"; }
~Foo() { std::cout << "~Foo()n"; }
};
int main(){
auto deleter = [](Foo* p) {
if(!p) { std::cout << "Calling deleter on nullptrn"; }
delete p;
};
std::shared_ptr<Foo> foo;
std::cout << "nWith non-null Foo:n";
foo = std::shared_ptr<Foo>(new Foo, deleter);
std::cout << "foo is " << (foo ? "not ":"") << "nulln";
std::cout << "use count=" << foo.use_count() << 'n';
foo.reset();
std::cout << "nWith nullptr and deleter:n";
foo = std::shared_ptr<Foo>(nullptr, deleter);
std::cout << "foo is " << (foo ? "not ":"") << "nulln";
std::cout << "use count=" << foo.use_count() << 'n';
foo.reset();
std::cout << "nWith nullptr, without deleter:n";
foo = std::shared_ptr<Foo>(nullptr);
std::cout << "foo is " << (foo ? "not ":"") << "nulln";
std::cout << "use count=" << foo.use_count() << 'n';
foo.reset();
}
输出为:
With non-null Foo:
Foo()
foo is not null
use count=1
~Foo()
With nullptr and deleter:
foo is null
use count=1
Calling deleter on nullptr
With nullptr, without deleter:
foo is null
use count=0
在这里,我们看到shared_ptr
在使用nullptr
和自定义删除器初始化时调用包含的删除器。 似乎,当使用自定义删除器初始化时,shared_ptr
认为它是"拥有"nullptr,因此在删除任何其他拥有的指针时尝试删除它。尽管未指定删除程序时不会发生这种情况。
这是预期的行为吗?如果是这样,这种行为背后的原因是什么?
tl;大卫:是的,这是有意的。
这是非常微妙的。
shared_ptr可以处于两种状态:
- "空":默认构造或重置; 没有所有权;
get()
可能会返回nullptr
(尽管存在一些更改此后置条件的 CTOR) - 不为空:持有指针
p
的所有权;get()
返回p
.
使用空指针构造shared_ptr
实际上会导致它不为空!get()
返回p
意味着get()
返回nullptr
,但这并不表示它为空。
由于默认删除器只是delete p
,并且delete nullptr
是无操作,因此这通常无关紧要。但是,如您所见,如果您提供自己的删除器,则可以观察到这种差异。
我不知道这是为什么。一方面,我可以看到在 nullptr 情况下阻止调用删除器的情况,因为人们通常认为shared_ptr(nullptr)
是"空的"(即使它在技术上不是);另一方面,如果删除者愿意,我可以看到让删除者做出此决定(附带分支开销)的情况。
您在此处包含对 null 的检查是正确的。
一些来自[util.smartptr.shared.const]
的法律术语:
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);
9) 要求:构造
d
和用std::move(d)
初始化的D
类型的删除器不得引发异常。表达式d(p)
应具有明确定义的行为,并且不应引发异常。A 应满足 CPP17 分配器要求(表 34)。10) 效果:构造一个拥有对象
p
和删除器d
的shared_ptr
对象。当T
不是数组类型时,第一个和第二个构造函数会启用带有p
的shared_from_this
。第二个和第四个构造函数应使用a
的副本来分配内存以供内部使用。如果引发异常,则调用d(p)
。11) 确保:
use_count() == 1 && get() == p
.
(请注意,对于!p
的情况没有豁免。
从[util.smartptr.shared.dest]
:
~shared_ptr();
1)效果:
- 如果
*this
为空或与另一个shared_ptr
实例(use_count() > 1
)共享所有权,则没有副作用。- 否则,如果
*this
拥有对象p
和删除程序d
,则调用d(p)
。- 否则,
*this
拥有一个指针p
,并调用delete p
。
旁注:我认为上述段落中"拥有对象"和"拥有指针"这两个短语之间的混淆是一个编辑问题。
我们还可以在cppreference.com的~shared_ptr
文章中看到这一点:
与
std::unique_ptr
不同,即使托管指针为空,也会调用std::shared_ptr
的删除器。
(请使用文档!
- 共享指针和具有自定义删除程序的唯一指针之间的语法差异背后的任何原因
- 如何知道何时调用删除以及何时调用 delete[] C++?
- C++变量在调用 x64 程序集函数后重置为 0
- 在unique_ptr<>中使用自定义删除程序 (curl_formfree())
- 如何从 CLion 的运行窗口中删除程序项目路径
- 亲.InsertAtHead() 调用创建程序崩溃
- shared_ptr的删除程序是否存储在自定义分配器分配的内存中?
- 用c++调用python程序
- 如何调用处理程序
- 获取调用函数调用的C++程序中的所有行号
- 真的有必要在这个指针上调用删除吗?
- 用于unique_ptr的有状态自定义删除程序
- 如果在 DLL 和调用应用程序中使用 GPGPU API,会发生什么情况
- std::shared_ptr 在空指针上调用非默认删除程序
- 调用应用程序libjingle,用户自动从花名册中删除.为什么?
- 删除程序的调用上下文
- 调用删除会导致程序冻结C++
- 调用删除时应用程序崩溃
- 用户界面 - C++:在事件驱动系统中间接调用“删除此内容”(例如在GUI应用程序中)
- 如果我在使用新的和结束的程序分配数据后没有调用删除运算符,会发生什么情况?