weak_ptr shared_ptr example c++ visual-c++ memory-management stl shared-ptr

c++ - example - ¿Es seguro usar STL(TR1) shared_ptr''s entre módulos(exes y dlls)?



shared_ptr example (5)

Sé que renovar algo en un módulo y eliminarlo en otro a menudo puede causar problemas en VC ++. Problemas con diferentes tiempos de ejecución. La combinación de módulos con tiempos de ejecución enlazados estáticamente y / o desajustes de versiones vinculados dinámicamente ambos puede atornillar cosas si no recuerdo mal.

Sin embargo, ¿es seguro usar el archivo std :: tr1 :: shared_ptr de VC ++ 2008 en todos los módulos?

Como solo hay una versión del tiempo de ejecución que sabe qué es un shared_ptr, mi único peligro (por ahora ...) es el enlace estático. Pensé que había leído que la versión de boost de un shared_ptr era segura de usar así, pero estoy usando la versión de Redmond ...

Estoy tratando de evitar tener una llamada especial para liberar objetos en el módulo de asignación. (o algo así como "eliminar esto" en la clase misma). Si todo esto parece un poco raro, lo estoy usando para pruebas unitarias. Si alguna vez ha intentado probar el código C ++ existente, puede comprender qué tan creativo debe ser a veces. Mi memoria es asignada por un EXE, pero finalmente será liberada en una DLL (si el recuento de referencias funciona de la manera que creo).


El mejor consejo que he visto sobre el tema general es que la memoria debe ser desasignada en el mismo contexto en que está asignada. Sin embargo, eso no impide que una biblioteca devuelva un puntero que el código de la aplicación debería liberar, así que diría que probablemente esté seguro al pasar el shared_ptr de esta manera, ya que es la misma situación general.

Si la semántica de su sistema significa que el puntero se transfiere realmente (en el sentido de la propiedad) de su exe a su dll, entonces un auto_ptr podría ser una mejor solución. Sin embargo, si su puntero es verdaderamente compartido, entonces shared_ptr es probablemente la mejor solución.


Liberar la memoria es seguro, siempre y cuando todo provenga del mismo contexto de administración de memoria . Has identificado el problema más común (diferentes tiempos de ejecución de C ++); tener montones separados es otro problema menos común que puedes encontrar.

Otro problema que no mencionó, pero que puede ser exacerbado por punteros compartidos, es cuando el código de un objeto existe en la DLL y es creado por la DLL, pero otro objeto fuera de la DLL termina con una referencia a la misma (a través de shared puntero). Si ese objeto se destruye después de que se descarga el archivo DLL (por ejemplo, si es un elemento estático a nivel de módulo, o si FreeLibrary() descarga el archivo DLL, el destructor del objeto compartido se bloqueará.

Esto puede morderle si intenta escribir complementos sin conexión y basados ​​en DLL. También es la razón por la que COM permite que las DLL decidan cuándo se pueden descargar, en lugar de dejar que los servidores COM exijan-descarguen.


Si está preocupado, use el formulario del constructor shared_ptr que toma un argumento de eliminación. El eliminador puede devolver la llamada al módulo que asignó el objeto para que la eliminación se produzca en el contexto adecuado.

La documentación de Boost afirma que es 100% compatible con TR1, por lo que esperamos que no haya nada engañoso al respecto:

http://www.boost.org/doc/libs/1_37_0/libs/smart_ptr/shared_ptr.htm#constructors


Supongo que es tan seguro como usar cualquiera de las clases en std en todos los módulos.

Es decir: debería ser seguro si los módulos usan exactamente la misma biblioteca de tiempo de ejecución y exactamente los mismos conmutadores y opciones del compilador.

Nunca use la biblioteca de tiempo de ejecución estática, ya que cada módulo obtendrá su propia instancia de todos los globales dentro de ella.


Estás empezando a ver qué increíblemente increíble shared_ptr es :)

Estar seguro entre los límites de DLL es exactamente lo que shared_ptr fue diseñado para ser (entre otras cosas, por supuesto).

Contrariamente a lo que otros han dicho, ni siquiera es necesario pasar un eliminador personalizado al construir el shared_ptr , ya que el valor predeterminado ya es algo así como

template <typename T> struct default_deleter { void operator()( T * t ) { delete t; } };

y

shared_ptr<Foo> foo( new Bar );

es equivalente a

shared_ptr<Foo> foo( new Bar, default_deleter<Bar>() );

(es decir, no existe un shared_ptr sin un eliminador).

Debido al borrado de tipo realizado en el eliminador, la delete que se llama siempre será la de la DLL que shared_ptr la instancia de shared_ptr , nunca la de la DLL donde la última shared_ptr queda fuera del alcance (es decir, el shared_ptr invoca al eliminador se llamarlo a través de un puntero a una función puesta allí por el shared_ptr original).

Compare esto con auto_ptr , que incrusta el operador delete directamente en su destructor (en línea), lo que significa que se utiliza la delete de la DLL que destruye el auto_ptr , creando los mismos problemas que borrar un puntero desnudo.

Por la misma técnica, las clases polimórficas que siempre se mantienen en shared_ptr s ni siquiera necesitan un destructor virtual, porque el eliminador siempre llamará al destructor correcto, incluso cuando el último shared_ptr para salir del alcance es una instancia para la clase base .