2009-01-14 6 views
12

Si utilizo la palabra clave new en mi biblioteca (que se construye de forma diferente a mi aplicación principal), cuando la elimino en mi aplicación principal con delete, ¿hay alguna posibilidad de que se produzca un bloqueo/error?C++ mezclar nuevo/eliminar entre libs?

Respuesta

9

yes indeedy. En particular, usted ve que los problemas con los montones de depuración/liberación son diferentes, y si su biblioteca utiliza la ubicación nueva o cualquier montón personalizado, tendrá un problema. El problema de depuración/liberación es, con mucho, el más común.

+0

Sí esto sucede –

+1

también puede ocurrir cuando librerías C++ están enlazados dentro de una DLL de Windows. – jmucchiello

6

Sí, lo hará. Una solución simple es proporcionar funciones Crear y Eliminar en su biblioteca a las que se pueda llamar desde la aplicación principal. La función Crear realizará la nueva y devolverá un puntero, que luego se pasará a la función Eliminar para su eliminación.

16

Depende. Si está hablando de una biblioteca estática, entonces probablemente esté bien: el código se ejecutará en el mismo contexto que el programa principal, utilizando la misma biblioteca de tiempo de ejecución de C++. Esto significa que new y delete usarán el mismo montón.

Si está hablando de una biblioteca compartida (una DLL), entonces probablemente no estará bien. El código que se ejecuta en el archivo DLL puede estar utilizando una biblioteca de tiempo de ejecución de C++ diferente, lo que significa que el diseño del montón será diferente. La DLL podría estar usando un montón diferente por completo.

Llamar delete (en el programa principal) en un puntero asignado por el DLL (o viceversa) dará lugar (en el mejor) un bloqueo inmediato o (en el peor) daño de memoria que llevará un tiempo rastrear .

Tienes un par de opciones. La primera es utilizar el patrón "método de fábrica" ​​para crear y eliminar estos objetos:

Foo *CreateFoo(); 
void DeleteFoo(Foo *p); 

Estos deben no ser implementados en el archivo de cabecera.

Como alternativa, se puede definir un método en el objeto Destroy:

class Foo 
{ 
    ~Foo(); 

public: 
    virtual void Destroy(); 
}; 

... otra vez, no implementar esto en el archivo de cabecera. Se podría aplicar así:

void Foo::Destroy() 
{ 
    delete this; 
    // don't do anything that accesses this object past this point. 
} 

Tenga en cuenta que el destructor de Foo es privada, por lo que hay que llamar Foo::Destroy.

Microsoft COM hace algo similar, donde define un método Release que elimina el objeto cuando su recuento de referencia cae a cero.

1

Tiene toda la razón de que hay un problema allí, pero para la mayoría de los casos hay una solución aún más simple que las otras respuestas (hasta ahora) han propuesto. Puede continuar usando new y delete libremente; todo lo que necesita hacer es sobrecargar new y delete para cada clase en su biblioteca que pueda usarse a través de los límites de la DLL.

Personalmente, acaba de definir una clase simple para proporcionar la funcionalidad necesaria:

class NewDelete 
{ 
    public: 
     void *operator new (size_t size); 
     void operator delete (void *memory); 
     void *operator new (size_t size, void *ptr); 
     void operator delete (void *memory, void *ptr); 
}; 

Mientras esos cuatro funciones miembro son definidos en la misma DLL, entonces cualquier clase que se deriva de esta clase es automáticamente "DLL-safe" - nuevo y eliminar se puede utilizar normalmente en ellos sin preocuparse por los límites de DLL.

+1

-1. He estado viendo este mismo tipo de idea, y he notado que esta práctica es bastante defectuosa. No puedo (aún) para hablar operador borrar, pero si el operador nuevo no puede asignar la memoria dentro del espacio DLL se eleva un std :: bad_alloc excepción, ya que las bibliotecas o archivos DLL compartidos no son reconocidos por el estándar de C++ el comportamiento no está definido. Como regla general, las excepciones no cruzarán los límites compartidos de la biblioteca o DLL. Sí, esto funciona si tanto usted como su cliente utilizan entornos de desarrollo que coinciden, pero en el momento en que uno cambie, todo explotará en la cara de sus clientes. – Geoff

+0

Debo aclarar en mi nota anterior: resulta que funciona con entornos de desarrollo de coincidencias (recientes) de MSVC, aunque no conozco ningún otro. – Geoff

4

es un problema que sólo he visto en Windows.

Los sistemas Unixish no tienen la costumbre de obligar a las bibliotecas compartidas para vincular a las diferentes versiones de la misma biblioteca dentro del mismo programa y todos los símbolos cargados son visibles a nivel mundial. Eso significa que si un objeto se asigna en una parte del código y se elimina en otra, ambos utilizan la misma biblioteca del sistema para hacerlo.

Tengo que decir, este problema Windows crea con sus diversas DLL de tiempo de ejecución C es realmente molesto y poco natural a un programador C. Mira la biblioteca C; tiene funciones como strdup que malloc la cadena y esperan que el programador llame gratis() sobre ella. Pero haga lo mismo en su propia biblioteca en Windows y simplemente espere la explosión. Tendrás que esperar también, porque no ocurrirá durante el desarrollo, sino solo después de que hayas dado el archivo DLL compilado a alguna otra fuente pobre.

+1

Esto no tiene nada que ver con el sistema operativo, pero la forma en que libc está vinculado. Trabajé en una tienda de Linux donde estuvimos enlazando estáticamente libc a nuestra aplicación, y nos lleva exactamente al mismo problema. –

+0

Si haces strdup y gratis en tu biblioteca, estás bien. Solo cuando haces strdup en un tiempo de ejecución y estás libre en otro, entonces hay un problema. Y Zan Lynz tiene razón, esto sucede en todas las plataformas si quiere/necesita múltiples tiempos de ejecución. –

3

Old New Thing ha cubierto esto antes. También da una lista de las principales soluciones de Microsoft.