Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145239

How does the custom deleter of std::unique_ptr work?

According to N3290, std::unique_ptr accepts a deleter argument in its constructor.

However, I can't get that to work with Visual C++ 10.0 or MinGW g++ 4.4.1 in Windows, nor with g++ 4.6.1 in Ubuntu.

I therefore fear that my understanding of it is incomplete or wrong. I can't see the point of a deleter argument that's apparently ignored, so can anyone provide a working example?

Preferably I'd like to see also how that works for unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).

Possibly with some wording from the standard to back up the example, i.e. that with whatever compiler you're using, it actually does what it's supposed to do?

Upvotes: 57

Views: 43603

Answers (4)

Philippe Cayouette
Philippe Cayouette

Reputation: 674

To complement all previous answers, there is a way to have a custom deleter without having to “pollute” the unique_ptr signature by having either a function pointer or something equivalent in it like this:

std::unique_ptr<MyType, myTypeDeleter> // not pretty

This is achievable by providing a full specialization to the std::default_delete class template, like this:

template<>
class std::default_delete<MyType>
{
public:
    void operator()(MyType *ptr) const
    {
        delete ptr;
    }
};

And now all std::unique_ptr<MyType> that “sees” this specialization will be deleted with it. Just be aware that it might not be what you want for all std::unique_ptr<MyType>, so chose carefully your solution.

Upvotes: 27

Cheers and hth. - Alf
Cheers and hth. - Alf

Reputation: 145239

My question has been pretty well answered already.

But just in case people wondered, I had the mistaken belief that a unique_ptr<Derived> could be moved to a unique_ptr<Base> and would then remember the deleter for the Derived object, i.e., that Base would not need to have a virtual destructor. That was wrong. I'd select Kerrek SB's comment as "the answer", except one cannot do that for a comment.

@Howard: the code below illustrates one way to achieve what I believed the cost of a dynamically assigned deleter had to mean that unique_ptr supported out of the box:

#include <iostream>
#include <memory>           // std::unique_ptr
#include <functional>       // function
#include <utility>          // move
#include <string>
using namespace std;

class Base
{
public:
    Base() { cout << "Base:<init>" << endl; }
    ~Base() { cout << "Base::<destroy>" << endl; }
    virtual string message() const { return "Message from Base!"; }
};

class Derived
    : public Base
{
public:
    Derived() { cout << "Derived::<init>" << endl; }
    ~Derived() { cout << "Derived::<destroy>" << endl; }
    virtual string message() const { return "Message from Derived!"; }
};

class BoundDeleter
{
private:
    typedef void (*DeleteFunc)( void* p );

    DeleteFunc  deleteFunc_;
    void*       pObject_;

    template< class Type >
    static void deleteFuncImpl( void* p )
    {
        delete static_cast< Type* >( p );
    }

public:
    template< class Type >
    BoundDeleter( Type* pObject )
        : deleteFunc_( &deleteFuncImpl< Type > )
        , pObject_( pObject )
    {}

    BoundDeleter( BoundDeleter&& other )
        : deleteFunc_( move( other.deleteFunc_ ) )
        , pObject_( move( other.pObject_ ) )
    {}

    void operator() (void*) const
    {
        deleteFunc_( pObject_ );
    }
};

template< class Type >
class SafeCleanupUniquePtr
    : protected unique_ptr< Type, BoundDeleter >
{
public:
    typedef unique_ptr< Type, BoundDeleter >    Base;

    using Base::operator->;
    using Base::operator*;

    template< class ActualType >
    SafeCleanupUniquePtr( ActualType* p )
        : Base( p, BoundDeleter( p ) )
    {}

    template< class Other >
    SafeCleanupUniquePtr( SafeCleanupUniquePtr< Other >&& other )
        : Base( move( other ) )
    {}
};

int main()
{
    SafeCleanupUniquePtr< Base >  p( new Derived );
    cout << p->message() << endl;
}

Upvotes: 11

Benjamin Lindley
Benjamin Lindley

Reputation: 103693

This works for me in MSVC10

int x = 5;
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; };
std::unique_ptr<int, decltype(del)> px(&x, del);

And on gcc 4.5, here

I'll skip going to the standard, unless you don't think that example is doing exactly what you'd expect it to do.

Upvotes: 46

Jagannath
Jagannath

Reputation: 4025

This works. The destruction happens properly.

class Base
{
    public:
     Base() { std::cout << "Base::Base\n"; }
     virtual ~Base() { std::cout << "Base::~Base\n"; }
};


class Derived : public Base
{
    public:
     Derived() { std::cout << "Derived::Derived\n"; }
     virtual ~Derived() { std::cout << "Derived::~Derived\n"; }
};

void Delete(const Base* bp)
{
    delete bp;
}

int main()
{
    std::unique_ptr<Base, void(*)(const Base*)> ptr = std::unique_ptr<Derived, void(*)(const Base*)>(new Derived(), Delete);
}

Upvotes: 6

Related Questions