2008-12-05 9 views
16

Sé que algo nuevo en un módulo y su eliminación 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.¿Es seguro utilizar STL (TR1) shared_ptr entre módulos (ex y dlls)

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 incluso qué es un shared_ptr, mi único peligro es la conexión estática (por ahora ...). 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 de C++ existente, puede comprender cómo creative necesita estar 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).

Respuesta

14

liberar la memoria es seguro, siempre y cuando todos ellos procedentes de la misma administración memoria contexto. 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 (a través del puntero compartido).Si ese objeto se destruye después de que se descarga el archivo DLL (por ejemplo, si es un módulo estático o si el archivo DLL se descarga explícitamente en FreeLibrary(), el destructor del objeto compartido se bloqueará.

Esto puede morder si intenta hacerlo escribir, plugins débilmente acoplados a base de DLL. es también la razón por la que COM permite DLL decidir cuándo pueden ser descargadas, en lugar de dejar que los servidores COM ellas la demanda de descarga.

+0

Sabía que las DLL cargadas dinámicamente podrían ser un problema, gracias por explicar el 'por qué'. –

2

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

Esto es: debería ser seguro si los módulos usan exactamente la misma biblioteca de tiempo de ejecución, y exactamente las mismas opciones y conmutadores del compilador.

Nunca utilice 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.

+0

Yo controlo los dos módulos involucrados, entonces estaré bien incluso si shared_ptr tiene este tipo de limitaciones. Pero, no quiero hacerlo si * podría * ser malo. – Aardvark

+0

Uso regularmente varios módulos donde los recursos se asignan en uno de ellos y se publican en otro. – dalle

+0

La parte importante es * si los módulos usan exactamente la misma biblioteca de tiempo de ejecución *. Incluso mezclar compilaciones de depuración y liberación es un boleto seguro al infierno. – MP24

2

El mejor consejo que he visto sobre el tema general es que la memoria debe desasignarse 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.

+0

Si tuviera que escribir un código para asegurarme de que estoy liberado en el mismo módulo, se frustra el propósito de shared_ptr. Puedo volver al modo nuevo/eliminar estándar ... (que es una opción válida). ¡Gracias por tomarse el tiempo para ayudar! – Aardvark

7

Si le preocupa, 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. documentación

de Boost afirma que es 100% compatible con TR1, así que espero que no hay nada erróneo en esto:

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

10

Estás comenzando a ver lo increíblemente increíble shared_ptr es :)

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

Al contrario de lo que otros han dicho, que ni siquiera tiene que pasar un Deleter personalizada al construir la shared_ptr, como el defecto ya es algo así como

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

y

shared_ptr<Foo> foo(new Bar); 

se equivalente a

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

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

Debido a la supresión de tipo realizado en el Deleter, la delete que se recibe la llamada siempre ser el uno de la DLL que instanciado la shared_ptr, no el de la DLL en el último shared_ptr sale del ámbito (es decir, el shared_ptr que invoca al eliminador lo llamará a través de un puntero a una función colocada allí por el shared_ptr original).

comparar esto con auto_ptr, que incrusta el operador delete directamente en su destructor (inline), que significa que se utiliza la delete de la DLL que destruye la auto_ptr, la creación de los mismos problemas que la eliminación de 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 fuera del alcance es instanciado para la clase base.

Cuestiones relacionadas