c++ - make_unique - Bueno, ¿cómo funciona el eliminador personalizado de std:: unique_ptr?
c++11 unique-ptr (4)
De acuerdo con N3290, std::unique_ptr
acepta un argumento delegador en su constructor.
Sin embargo, no puedo conseguir que funcione con Visual C ++ 10.0 o MinGW g ++ 4.4.1 en Windows, ni con g ++ 4.6.1 en Ubuntu.
Por lo tanto, me temo que mi comprensión de esto es incompleta o incorrecta, no puedo ver el punto de un argumento eliminador que aparentemente es ignorado, entonces ¿alguien puede dar un ejemplo de trabajo?
Preferiblemente, me gustaría ver también cómo funciona para unique_ptr<Base> p = unique_ptr<Derived>( new Derived )
.
Posiblemente con algunas palabras del estándar para hacer una copia de seguridad del ejemplo, es decir, que con el compilador que está utilizando, ¿realmente hace lo que se supone que debe hacer?
Esto funciona para mí en MSVC10
int x = 5;
auto del = [](int * p) { std::cout << "Deleting x, value is : " << *p; };
std::unique_ptr<int, decltype(del)> px(&x, del);
Y en gcc 4.5, here
Voy a omitir ir al estándar, a menos que no piense que ese ejemplo está haciendo exactamente lo que usted esperaría que hiciera.
Esto funciona. La destrucción ocurre correctamente
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);
}
Mi pregunta ya ha sido respondida bastante bien.
Pero por si la gente se preguntaba, tenía la creencia errónea de que un unique_ptr<Derived>
podía moverse a un unique_ptr<Base>
y luego recordaría el eliminador del objeto Derived
, es decir, que Base
no necesitaría tener un destructor virtual . Eso estuvo mal. Seleccionaría el comentario de Kerrek SB como "la respuesta", excepto que uno no puede hacer eso para un comentario.
@Howard : el siguiente código ilustra una forma de lograr lo que creía que el costo de una eliminación delegada asignada dinámicamente tenía que significar que unique_ptr
compatible de manera unique_ptr
:
#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;
}
Aclamaciones,
Para complementar todas las respuestas anteriores, hay una forma de tener un eliminador personalizado sin tener que "contaminar" la firma unique_ptr al tener un puntero a la función o algo equivalente en él como este:
std::unique_ptr< MyType, myTypeDeleter > // not pretty
Esto se puede lograr proporcionando una especialización a la clase de plantilla std :: default_delete, como esta:
namespace std
{
template<>
class default_delete< MyType >
{
public:
void operator()(MyType *ptr)
{
delete ptr;
}
};
}
Y ahora se std::unique_ptr< MyType >
todo std::unique_ptr< MyType >
que "vea" esta especialización. Solo tenga en cuenta que puede que no sea lo que desea para todos los std::unique_ptr< MyType >
, así que elija cuidadosamente su solución.