本文介绍了std :: shared_ptr在空指针上调用非默认删除器的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

请参见以下示例:

  #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可以处于两种状态:

使用空指针构造 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:

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在空指针上调用非默认删除器的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

07-01 12:49