Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does it make sense to check for nullptr in custom deleter of shared_ptr?

I've seen some code that uses std::shared_ptr with a custom deleter that test the argument for nullptr, for example, MyClass which has a close() method and is constructed with some CreateMyClass:

auto pMyClass = std::shared_ptr<MyClass>(CreateMyClass(), 
                                        [](MyClass* ptr)
                                        { 
                                            if(ptr) 
                                                ptr->close(); 
                                        });

Does it make sense to test ptr for null-ness in the deleter? Can this happen? how?

like image 682
ZivS Avatar asked Mar 22 '17 20:03

ZivS


People also ask

Can shared_ptr be Nullptr?

A null shared_ptr does serve the same purpose as a raw null pointer. It might indicate the non-availability of data. However, for the most part, there is no reason for a null shared_ptr to possess a control block or a managed nullptr .

In what situation is a shared_ptr more appropriate than a unique_ptr?

Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.

Can a unique_ptr be Nullptr?

Nullability - a scoped_ptr or unique_ptr can be null, a value object can never be. Polymorphism - a value object is always exactly its static type, but you can substitute in different derived types for a unique_ptr. The previously-held object is automatically destroyed when you do this.

What is an empty shared_ptr?

Empty shared_ptr can be constructed with default constructor or with constructor that takes nullptr . Non-empty null shared_ptr has control block that can be shared with other shared_ptr s. Copy of non-empty null shared_ptr is shared_ptr that shares the same control block as original shared_ptr so use count is not 0.


2 Answers

The constructor std::shared_ptr<T>::shared_ptr(Y*p) has the requirement that delete p is a valid operation. This is a valid operation when p equals nullptr.

The constructor std::shared_ptr<T>::shared_ptr(Y*p, Del del) has the requirement that del(p) is a valid operation.

If your custom deleter cannot handle p being equal to nullptr then it is not valid to pass a null p in the constructor of shared_ptr.

The constructor you offer as an example can be better presented, thus:

#include <memory>

struct MyClass {
    void open() {
        // note - may throw
    };

    void close() noexcept {
        // pre - is open
    }
};

struct Closer
{
    void operator()(MyClass* p) const noexcept
    {
        p->close();
        delete p;  // or return to pool, etc
    }
};

auto CreateMyClass() -> std::unique_ptr<MyClass, Closer>
{
    // first construct with normal deleter
    auto p1 = std::make_unique<MyClass>();

    // in case this throws an exception.
    p1->open();

    // now it's open, we need a more comprehensive deleter
    auto p = std::unique_ptr<MyClass, Closer> { p1.release(), Closer() };
    return p;
}

int main()
{
    auto sp = std::shared_ptr<MyClass>(CreateMyClass());
}

Note that it is now not possible for the shared_ptr to own a null object.

like image 63
Richard Hodges Avatar answered Oct 24 '22 08:10

Richard Hodges


Yes, it makes sense actually. Suppose CreateMyClass returns nullptr. Reference count of pMyClass (use_count) becomes 1. When pMyClass will be destroyed, following will happens:

If *this owns an object and it is the last shared_ptr owning it, the object is destroyed through the owned deleter.

So if custom deleter dereferencing a pointer that holded by shared_ptr (ptr->close() in your code) then it should take care of nullptr checking.

Notice that empty shared_ptr is not the same as null shared_ptr.

like image 7
em2er Avatar answered Oct 24 '22 08:10

em2er