2010-02-19 9 views
17

Me he estado enseñando a mí mismo los punteros inteligentes que son parte de C++ 0x y encontré algo que me parece incongruente. Específicamente, cómo se manejan las políticas de destrucción de unique_ptr <> y shared_ptr <>.unique_ptr <> v shared_ptr <> en lo que respecta a la política de destrucción

Para unique_ptr <>, puede especializar std :: default_delete <> y, a partir de ese momento, a menos que solicite explícitamente una política de destrucción diferente, se utilizará la nueva configuración predeterminada.

considerar lo siguiente:

struct some_c_type; 

some_c_type *construct_some_c_type(); 
void destruct_some_c_type(some_c_type *); 

namespace std { 
    template <> struct default_delete<some_c_type> { 
     void operator()(some_c_type *ptr) { 
      destruct_some_c_type(ptr); 
     } 
    }; 
} 

Ahora, una vez que esté en su lugar, unique_ptr <> va a utilizar la política de destrucción apropiada de manera predeterminada:

// Because of the specialization, this will use destruct_some_c_type 
std::unique_ptr<some_c_type> var(construct_some_c_type()); 

Ahora compare esto a shared_ptr <>. Con shared_ptr <>, tiene que solicitar explícitamente la política adecuada destrucción o el valor predeterminado es utilizar el operador delete:

// error, will use operator delete 
std::shared_ptr<some_c_type> var(construct_some_c_type()); 

// correct, must explicitly request the destruction policy 
std::shared_ptr<some_c_type> var(construct_some_c_type(), 
           std::default_delete<some_c_type>()); 

dos preguntas.

  1. ¿Es correcto que shared_ptr <> requiere que se especifique la política de destrucción cada vez que se usa o me falta algo?
  2. Si no me falta algo, ¿alguna idea de por qué los dos son diferentes?

P.S. La razón por la que me importa esto es que mi compañía hace mucha programación mixta en C y C++. El código de C++ a menudo necesita usar objetos de estilo C, por lo que la facilidad de especificar una política de destrucción predeterminada diferente es bastante importante para mí.

+2

¿Se supone que es 'std :: default_delete ()'? Y en una nota al margen, a menos que esté arreglado en C++ 0x y estoy a punto de descubrirlo, los tres son molestos. – GManNickG

+0

@GMan - gracias, eso fue un error tipográfico. ¿Por qué los considera molestos (personalmente, considero que unique_ptr <> es un diseño bastante limpio)? –

+1

@RSamuel: Quise decir el análisis molesto; en C++ 03, todas son declaraciones de funciones. – GManNickG

Respuesta

4

Creo que la pregunta se reduce a por qué std :: shared_ptr no puede tener un eliminador asociado (en cuyo caso simplemente llama al delete) en lugar de construir un std::default_delete de manera predeterminada. (No hay idea. Si la intención era que default_delete fuera para una especialización, uno esperaría que la usara el shared_ptr.)

De lo contrario, hay compensaciones.

Es mejor tener menos argumentos de plantilla. La referencia de Boost menciona que esto permite que una fábrica cambie el esquema de asignación sin que el cambio afecte al usuario de la fábrica. Por otra parte, se supone que unique_ptr es muy liviano ¿Cómo almacenaría el eliminador con una sobrecarga de espacio cero (en el caso de un funtor sin miembros) si no era parte del tipo (GCC usa una tupla, donde los objetos sin memoría no ocupan espacio en la memoria)?


Subjetivamente, creo que prefiero:

unique_ptr<FILE, FCloser> f(fopen(x, y)); 

a

unique_ptr<FILE> f(fopen(x, y)); //default_delete<FILE> has been specialized 

En el primer caso no hay nada que adivinar. Si el recurso no proviene de new o new[], se debe proporcionar explícitamente un eliminador.

+1

Estoy de acuerdo: explícito es mejor que implícito. – dalle

+0

@UncleBens: un problema con la discusión implícita/explícita es que el estándar permite específicamente implícitos que siempre compilarán pero podrían causar un comportamiento indefinido. Si siempre ha tenido que especificar el eliminador incluso para un simple 'delete' o' delete [] 'que puede estar bien. Pero tal como es, es fácil de olvidar, la construcción tendrá éxito y tal error podría pasar desapercibido. –

+1

Especialícelo para FILE y similares a static_assert? :) Pero incluso si lo especializas, ¿deberás recordar incluir el encabezado con las especializaciones? – UncleBens

Cuestiones relacionadas