web-dev-qa-db-fra.com

Eh bien, comment fonctionne le suppresseur personnalisé de std :: unique_ptr?

Selon N3290, std::unique_ptr Accepte un argument deleter dans son constructeur.

Cependant, je ne peux pas faire fonctionner cela avec Visual C++ 10.0 ou MinGW g ++ 4.4.1 dans Windows, ni avec g ++ 4.6.1 dans Ubuntu.

J'ai donc peur que ma compréhension soit incomplète ou erronée, je ne vois pas l'intérêt d'un argument plus délicat qui est apparemment ignoré, alors quelqu'un peut-il fournir un exemple de travail?

De préférence, j'aimerais voir comment cela fonctionne pour unique_ptr<Base> p = unique_ptr<Derived>( new Derived ).

Peut-être avec un libellé de la norme pour sauvegarder l'exemple, c'est-à-dire qu'avec le compilateur que vous utilisez, il fait ce qu'il est censé faire?

55

Cela fonctionne pour moi dans MSVC10

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

Et sur gcc 4.5, ici

Je vais passer à la norme, à moins que vous ne pensiez pas que cet exemple fait exactement ce que vous attendez.

45
Benjamin Lindley

Pour compléter toutes les réponses précédentes, il existe un moyen d'avoir un suppresseur personnalisé sans avoir à "polluer" la signature unique_ptr en ayant soit un pointeur de fonction soit quelque chose d'équivalent comme ceci:

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

Ceci est réalisable en fournissant une spécialisation à la classe de modèle std :: default_delete, comme ceci:

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

Et maintenant tout std::unique_ptr< MyType > qui "voit" cette spécialisation sera supprimée avec elle. Sachez simplement que ce n'est peut-être pas ce que vous voulez pour tous std::unique_ptr< MyType >, choisissez donc soigneusement votre solution.

24

On a déjà assez bien répondu à ma question.

Mais juste au cas où les gens se demanderaient, j'avais la croyance erronée qu'un unique_ptr<Derived> pourrait être déplacé vers un unique_ptr<Base> et se souviendrait alors du suppresseur de l'objet Derived, c'est-à-dire que Base n'aurait pas besoin d'avoir un destructeur virtuel. C'était faux. Je choisirais commentaire de Kerrek SB comme "la réponse", sauf qu'on ne peut pas faire ça pour un commentaire.

@ Howard : le code ci-dessous illustre une façon d'atteindre ce que je pensais que le coût d'un déléteur attribué dynamiquement devait signifier que unique_ptr pris en charge immédiatement:

#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;
}

À votre santé,

10

Cela marche. La destruction se passe correctement.

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);
}
6
Jagannath