问题描述
请参见以下示例:
#include< iostream>#include< memory>Foo类{上市:Foo(){std :: cout<<"Foo()\ n";}〜Foo(){std :: cout<<〜Foo()\ n";}};int main(){自动删除器= [](Foo * p){if(!p){std :: cout<<在nullptr上调用deleter \ n";}删除p;};std :: shared_ptr< Foo>foo;std :: cout<<"\ n使用非空Foo:\ n";foo = std :: shared_ptr< Foo>(new Foo,deleter);std :: cout<<"foo是"<<(foo?"not":")<<"null \ n";std :: cout<<使用count ="<<foo.use_count()<<'\ n';foo.reset();std :: cout<<"\ n使用nullptr和deleter:\ n";foo = std :: shared_ptr< Foo>(nullptr,deleter);std :: cout<<"foo是"<<(foo?"not":")<<"null \ n";std :: cout<<使用count ="<<foo.use_count()<<'\ n';foo.reset();std :: cout<<"\ n使用nullptr,不使用删除器:\ n";foo = std :: shared_ptr< Foo>(nullptr);std :: cout<<"foo是"<<(foo?"not":")<<"null \ n";std :: cout<<使用count ="<<foo.use_count()<<'\ n';foo.reset();}
输出为:
对于非null Foo:Foo()foo不为null使用次数= 1〜Foo()使用nullptr和deleter:foo为空使用次数= 1在nullptr上调用Deleter使用nullptr,不使用删除器:foo为空使用计数= 0
在这里,我们看到 shared_ptr
在使用 nullptr
和自定义删除器初始化包含的删除器时会调用它.似乎,当使用自定义删除程序初始化时, shared_ptr
认为它是拥有"的nullptr,因此在删除任何其他拥有的指针时会尝试将其删除.虽然在没有指定删除器的情况下不会发生.
这是预期的行为吗?如果是这样,此行为背后的原因是什么?
tl; dr:是的,这是有目的的.
这很微妙.
shared_ptr可以处于两种状态:
- 空":默认构造或重置;不拥有所有权;
get()
可能会返回nullptr
(尽管存在一些可更改此后置条件的ctor ) - 不为空:持有指针
p
的所有权;get()
返回p
.
使用空指针构造 shared_ptr
实际上会导致它不是空的! get()
返回 p
的意思是 get()
返回 nullptr
,但这并不能使其为空./p>
由于默认的删除程序只执行 delete p
的操作,而 delete nullptr
的操作则是无操作的,因此这通常无关紧要.但是,正如您所看到的,如果您提供自己的删除器,则可以观察到这种差异.
我确切不知道为什么为什么.一方面,我可以看到在nullptr情况下防止删除程序被调用的情况,因为通常认为 shared_ptr(nullptr)
是空的"(即使从技术上讲不是这样).另一方面,我可以看到一个让删除器根据需要做出决定的情况(带有分支的开销).
您应该在此处包括对null的检查.
[util.smartptr.shared.const]
中的一些法文:
(请注意,!p
的情况没有豁免.)
并且来自 [util.smartptr.shared.dest]
:
旁注:我认为以上段落中拥有一个对象"和拥有一个指针"这两个短语之间的混淆是一个编辑问题.
我们还可以在 cppreference.com的〜上看到此文档shared_ptr
文章:
(请使用文档!)
See this example :
#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 nullptr\n"; }
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 ":"") << "null\n";
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 ":"") << "null\n";
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 ":"") << "null\n";
std::cout << "use count=" << foo.use_count() << '\n';
foo.reset();
}
The output is :
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
Here we see that shared_ptr
calls the contained deleter when it is initialized with nullptr
and a custom deleter.It seems that, when initialized with a custom deleter, shared_ptr
considers it is "owning" nullptr and thus tries to delete it when it would delete any other owned pointer. Though it does not happen when no deleter is specified.
Is this intended behavior ? If so, what is the reason behind this behavior ?
tl;dr: Yes, it's intended.
This is pretty subtle.
A shared_ptr can be in two states:
- "empty": default-constructed or reset; holds no ownership;
get()
may returnnullptr
(although some ctors exist which change this postcondition) - not empty: holds ownership of a pointer
p
;get()
returnsp
.
Constructing a shared_ptr
with a null pointer actually leads to it being not-empty! get()
returning p
means get()
returning nullptr
, but that doesn't make it empty.
Since the default deleter just does delete p
, and delete nullptr
is a no-op, this doesn't usually matter. But, as you have seen, you can observe this difference if you provide your own deleter.
I don't know exactly why this is. On the one hand I can see a case for preventing a deleter from being invoked in the nullptr case because one generally considers a shared_ptr(nullptr)
to be "empty" (even though it technically is not); on the other hand, I can see a case for letting the deleter make this decision (with the accompanying overhead of a branch) if it wants to.
You're right to include a check for null here.
Some legalese from [util.smartptr.shared.const]
:
(Notice that there is no exemption for the case that !p
.)
And from [util.smartptr.shared.dest]
:
Sidenote: I think the confusion between the phrases "owns an object" and "owns a pointer" in the above passages is an editorial problem.
We can also see this documented on cppreference.com's ~shared_ptr
article:
(Please use documentation!)
这篇关于std :: shared_ptr在空指针上调用非默认删除器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!