2008-11-17 9 views
39

¿Tiene que pasar eliminar el mismo puntero que fue devuelto por nuevo, o puede pasarlo un puntero a uno de los tipos básicos de clases? Por ejemplo:¿Eliminar trabajo con punteros a la clase base?

class Base 
{ 
public: 
    virtual ~Base(); 
    ... 
}; 

class IFoo 
{ 
public: 
    virtual ~IFoo() {} 
    virtual void DoSomething() = 0; 
}; 

class Bar : public Base, public IFoo 
{ 
public: 
    virtual ~Bar(); 
    void DoSomething(); 
    ... 
}; 

Bar * pBar = new Bar; 
IFoo * pFoo = pBar; 
delete pFoo; 

Por supuesto, esto se simplifica enormemente. Lo que realmente quiero hacer es crear un contenedor lleno de boost :: shared_ptr y pasarlo a algún código que lo elimine del contenedor cuando esté terminado. Este código no sabrá nada de la implementación de Bar o Base, y se basará en el operador de eliminación implícita en el destructor shared_ptr para hacer lo correcto.

¿Es posible que esto funcione? Mi intuición dice que no, ya que los punteros no tendrán la misma dirección. Por otro lado, un dynamic_cast < Bar *> debería funcionar, por lo que en algún lugar el compilador está almacenando suficiente información para resolverlo.


Gracias por la ayuda, todos los que respondieron y comentaron. Ya sabía la importancia de los destructores virtuales, como se muestra en mi ejemplo; después de ver la respuesta, lo pensé un poco, y me di cuenta de que el motivo completo para un destructor virtual es este escenario exacto. Por lo tanto, tenía que funcionar. Me sorprendió la ausencia de un medio visible de convertir el puntero al original. Un poco más de pensamiento me llevó a creer que había un medio invisible, y teoricé que el destructor estaba devolviendo el verdadero puntero para que delete se liberara. Investigando el código compilado de Microsoft VC++ confirmó mi sospecha cuando vi esta línea de la base de ~:

mov eax, DWORD PTR _this$[ebp] 

Tracing el ensamblador reveló que este era el puntero se pasa a la función de borrado. Misterio resuelto.

He arreglado el ejemplo para agregar el destructor virtual a IFoo, fue un simple descuido. Gracias de nuevo a todos los que lo señalaron.

Respuesta

52

Sí, va a trabajar, si y sólo si el destructor de la clase base es virtual, lo que usted ha hecho para la clase Base base, pero no para la clase IFoo base. Si el destructor de la clase base es virtual, cuando llama al operator delete en el puntero de la clase base, usa el despacho dinámico para descubrir cómo eliminar el objeto al buscar el destructor de clase derivado en la tabla de funciones virtuales.

En su caso de herencia múltiple, solo funcionará si la clase base por la que lo está eliminando tiene un destructor virtual; está bien que las otras clases base no tengan un destructor virtual, pero solo si no intentas eliminar cualquier objeto derivado a través de esos otros punteros de clase base.

+0

Sé la importancia de un destructor virtual para destruir el objeto correctamente, por eso lo incluí en el ejemplo; ¿Estás diciendo que también hará que se libere la memoria adecuada? Y también necesito un destructor virtual para IFoo? –

+0

En realidad, está bien eliminar cualquier referencia siempre que la clase más básica tenga un destructor virtual. Una vez que un destructor sea virtual, todas las implementaciones derivadas tendrán un destructor virtual, esté o no especificado. C++ es divertido. – JaredPar

+3

La memoria real probablemente se liberará incluso sin el destructor virtual. – Eclipse

3

Esto no se relaciona con su ejemplo dado, pero como mencionó que está realmente interesado en el comportamiento de shared_ptr al eliminar su objeto propiedad, podría estar interesado en usar shared_ptr 'eliminador'.

Si el objeto propiedad de shared_ptr necesita un manejo especial cuando se elimina, puede especificar un 'eliminador' para cualquier shared_ptr<> en particular. El eliminador no es parte del tipo, es un atributo de una instancia shared_ptr<>, por lo que su contenedor de objetos shared_ptr<> podría tener algunos objetos con diferentes eliminadores.Esto es lo que los documentos Boost dicen sobre el shared_ptr<> Deleter:

deallocators personalizados permiten una función de fábrica devolver un shared_ptr a aislar al usuario de su estrategia de asignación de memoria . Dado que el descriptor de distribución no es parte del tipo, cambiando la estrategia de asignación no no rompe la compatibilidad de fuente o binaria , y no requiere una recompilación de cliente . Por ejemplo, un deallocator "no-op" es útil cuando devolver un shared_ptr a un objeto estáticamente asignado, y otras variaciones permitir un shared_ptr para ser utilizado como una envoltura para otro puntero inteligente, facilitar la interoperabilidad.

Sería más limpia si se puede modificar IFoo tener un destructor virtual ya que usted está planeando para eliminar los objetos que son subclases de la misma a través de una referencia o puntero IFoo. Pero si está atascado con un IFoo que no se puede corregir, entonces si desea usar shared_ptr<IFoo> en su contenedor, pero que apunte a Bar, puede crear la instancia shared_ptr con un eliminador que realiza un downcast a Bar* y luego hace la operación de eliminación. Los Downcasts se consideran de mala calidad, pero esta técnica podría ser algo que podrías usar en un enlace.

Cuestiones relacionadas