Pautas generales para evitar pérdidas de memoria en C ++ [cerrado]


130

¿Cuáles son algunos consejos generales para asegurarse de que no pierda memoria en los programas de C ++? ¿Cómo puedo averiguar quién debe liberar memoria que se ha asignado dinámicamente?


26
Me parece bastante constructivo.
Shoerob

11
Esto es constructivo. Y las respuestas están respaldadas por hechos, experiencia, referencias, etc. ¡¡Y vea el número de votos a favor / respuestas ... !!
Samitha Chathuranga

Respuestas:



200

Respaldo a fondo todos los consejos sobre RAII y los punteros inteligentes, pero también me gustaría agregar un consejo de nivel ligeramente más alto: la memoria más fácil de administrar es la memoria que nunca asignó. A diferencia de lenguajes como C # y Java, donde casi todo es una referencia, en C ++ debe colocar objetos en la pila siempre que pueda. Como he visto que varias personas (incluido el Dr. Stroustrup) señalan, la razón principal por la que la recolección de basura nunca ha sido popular en C ++ es que C ++ bien escrito no produce mucha basura en primer lugar.

No escribas

Object* x = new Object;

o incluso

shared_ptr<Object> x(new Object);

cuando solo puedes escribir

Object x;

34
Desearía poder darle a esto un +10. Este es el mayor problema que veo con la mayoría de los programadores de C ++ en la actualidad, y supongo que es porque aprendieron Java antes que C ++.
Kristopher Johnson

Punto muy interesante - me había preguntado por qué tengo problemas de gestión de memoria en C ++ por lo tanto con menos frecuencia que en otros idiomas, pero ahora veo por qué: lo que realmente permite a la materia va a la pila como la vainilla C
ArtOfWarfare

Entonces, ¿qué haces si escribes Object x; y luego quieres tirar x? digamos x fue creado en el método principal.
Yamcha

3
@ user1316459 C ++ le permite crear ámbitos sobre la marcha también. Todo lo que tiene que hacer es envolver la vida útil de x dentro de llaves de la siguiente manera: {Object x; x.DoSomething; }. Después del '}' final, se llamará al destructor de x para liberar cualquier recurso que contenga. Si x, en sí mismo, es la memoria que se asignará en el montón, sugiero que lo envuelva en un unique_ptr para que se limpie de manera fácil y adecuada.
David Peterson

1
Robert: si. Ross no dijo "Nunca escriba [código que contenga nuevo]", dijo "No escriba [eso] cuando puede simplemente [ponerlo en la pila]". Los objetos grandes en el montón continuarán siendo la llamada correcta en la mayoría de las situaciones, especialmente para el código de alto rendimiento.
codetaku

104

Use RAII

  • Olvídese de la recolección de basura (use RAII en su lugar). Tenga en cuenta que incluso el recolector de basura también puede filtrarse (si olvida "anular" algunas referencias en Java / C #), y que el recolector de basura no lo ayudará a deshacerse de los recursos (si tiene un objeto que adquirió un identificador para un archivo, el archivo no se liberará automáticamente cuando el objeto se salga del alcance si no lo hace manualmente en Java, o si usa el patrón "desechar" en C #).
  • Olvídese de la regla de "un retorno por función" . Este es un buen consejo de C para evitar fugas, pero está desactualizado en C ++ debido a su uso de excepciones (use RAII en su lugar).
  • Y aunque el "Patrón Sandwich" es un buen consejo de C, está desactualizado en C ++ debido a su uso de excepciones (use RAII en su lugar).

Esta publicación parece ser repetitiva, pero en C ++, el patrón más básico para saber es RAII .

Aprenda a usar punteros inteligentes, tanto desde boost, TR1 o incluso el auto_ptr bajo (pero a menudo lo suficientemente eficiente) (pero debe conocer sus limitaciones).

RAII es la base de la seguridad de excepción y la eliminación de recursos en C ++, y ningún otro patrón (sándwich, etc.) le proporcionará ambos (y la mayoría de las veces, no le dará ninguno).

Vea a continuación una comparación de código RAII y no RAII:

void doSandwich()
{
   T * p = new T() ;
   // do something with p
   delete p ; // leak if the p processing throws or return
}

void doRAIIDynamic()
{
   std::auto_ptr<T> p(new T()) ; // you can use other smart pointers, too
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

void doRAIIStatic()
{
   T p ;
   // do something with p
   // WON'T EVER LEAK, even in case of exceptions, returns, breaks, etc.
}

Sobre RAII

Para resumir (después del comentario de Ogre Psalm33 ), RAII se basa en tres conceptos:

  • Una vez que se construye el objeto, ¡simplemente funciona! Adquirir recursos en el constructor.
  • ¡La destrucción de objetos es suficiente! Haz recursos gratis en el destructor.
  • ¡Se trata de ámbitos! Los objetos de ámbito (ver el ejemplo doRAIIStatic anterior) se construirán en su declaración, y se destruirán en el momento en que la ejecución salga del ámbito, sin importar cómo salga (retorno, interrupción, excepción, etc.).

Esto significa que en el código C ++ correcto, la mayoría de los objetos no se construirán newy se declararán en la pila. Y para aquellos construidos usando new, todos tendrán un alcance (por ejemplo, conectado a un puntero inteligente).

Como desarrollador, esto es muy poderoso, ya que no tendrá que preocuparse por el manejo manual de recursos (como se hace en C, o para algunos objetos en Java que hacen un uso intensivo de try/ finallypara ese caso) ...

Editar (2012-02-12)

"los objetos con alcance ... serán destruidos ... sin importar la salida", eso no es del todo cierto. Hay formas de engañar a RAII. cualquier sabor de terminar () omitirá la limpieza. exit (EXIT_SUCCESS) es un oxímoron en este sentido.

- Wilhelmtell

Wilhelmtell tiene toda la razón al respecto: hay formas excepcionales de engañar a la RAII, todo lo cual lleva a que el proceso se detenga abruptamente.

Esas son formas excepcionales porque el código C ++ no está lleno de terminación, salida, etc., o en el caso de excepciones, queremos una excepción no controlada para bloquear el proceso y volcar su imagen de memoria como está, y no después de la limpieza.

Pero aún debemos conocer esos casos porque, aunque rara vez suceden, aún pueden suceder.

(¿quién llama terminateo exiten código C ++ casual? ... Recuerdo haber tenido que lidiar con ese problema cuando jugaba con GLUT : esta biblioteca está muy orientada a C, yendo tan lejos como a diseñarla activamente para dificultar las cosas a los desarrolladores de C ++ como no preocuparse sobre la asignación de datos asignados , o tener decisiones "interesantes" sobre no volver nunca de su ciclo principal ... no comentaré sobre eso) .


¿No debe la clase T usar RAII para asegurarse de que doRAIIStatic () no pierda memoria? Por ejemplo T p (); p.doSandwich (); Aunque realmente no sé mucho sobre esto.
Daniel O

@Ogre Psalm33: Gracias por el comentario. Por supuesto que tienes razón. Agregué ambos enlaces a la página de Wikipedia RAII y un pequeño resumen de lo que es RAII.
paercebal

1
@ Shiftbit: Tres formas, en orden de preferencia: _ _ _ 1. Coloque el objeto real dentro del contenedor STL. _ _ _ 2. Coloque punteros inteligentes (shared_ptr) de objetos dentro del contenedor STL. _ _ _ 3. Coloque punteros sin procesar dentro del contenedor STL, pero envuelva el contenedor para controlar cualquier acceso a los datos. El contenedor se asegurará de que el destructor libere los objetos asignados, y los accesores del contenedor se asegurarán de que nada se rompa al acceder / modificar el contenedor.
paercebal

1
@Robert: en C ++ 03, usaría doRAIIDynamic en una función que debe otorgar la propiedad a una función secundaria o primaria (o alcance global). O cuando recibe una interfaz para un objeto polimorfo a través de una fábrica (devuelve un puntero inteligente, si está escrito correctamente). En C ++ 11, este no es el caso porque puede hacer que su objeto sea móvil, por lo que es más fácil otorgar la propiedad de un objeto declarado en la pila ...
paercebal

2
@Robert: ... Tenga en cuenta que declarar un objeto en la pila no significa que el objeto no use el montón internamente (tenga en cuenta la doble negación ... :-) ...). Por ejemplo, std :: string implementado con Small String Optimization tendrá un búfer "en la pila de la clase" para cadenas pequeñas (~ 15 caracteres), y usará un puntero a una memoria en el montón para cadenas más grandes ... Pero desde el exterior, std :: string sigue siendo un tipo de valor que declara (generalmente) en la pila y usa como usaría un número entero (en lugar de: como usaría una interfaz para una clase polimorfa).
Paercebal

25

Querrá ver los punteros inteligentes, como los punteros inteligentes de boost .

En vez de

int main()
{ 
    Object* obj = new Object();
    //...
    delete obj;
}

boost :: shared_ptr se eliminará automáticamente una vez que el recuento de referencia sea cero:

int main()
{
    boost::shared_ptr<Object> obj(new Object());
    //...
    // destructor destroys when reference count is zero
}

Tenga en cuenta mi última nota, "cuando el recuento de referencias es cero, que es la parte más genial. Entonces, si tiene varios usuarios de su objeto, no tendrá que hacer un seguimiento de si el objeto todavía está en uso. Una vez que nadie se refiera a su puntero compartido, se destruye.

Sin embargo, esto no es una panacea. Aunque puede acceder al puntero base, no querrá pasarlo a una API de terceros a menos que esté seguro de lo que estaba haciendo. Muchas veces, su "publicación" de cosas en algún otro hilo para el trabajo que se realizará DESPUÉS de que finalice la creación del alcance. Esto es común con PostThreadMessage en Win32:

void foo()
{
   boost::shared_ptr<Object> obj(new Object()); 

   // Simplified here
   PostThreadMessage(...., (LPARAM)ob.get());
   // Destructor destroys! pointer sent to PostThreadMessage is invalid! Zohnoes!
}

Como siempre, usa tu gorra de pensamiento con cualquier herramienta ...



11

La mayoría de las pérdidas de memoria son el resultado de no ser claros acerca de la propiedad y la vida útil de los objetos.

Lo primero que debe hacer es asignar en la Pila siempre que pueda. Esto se ocupa de la mayoría de los casos en los que necesita asignar un solo objeto para algún propósito.

Si necesita 'nuevo' un objeto, la mayoría de las veces tendrá un único propietario obvio para el resto de su vida útil. Para esta situación, tiendo a usar un montón de plantillas de colecciones que están diseñadas para 'poseer' objetos almacenados en ellos por puntero. Se implementan con el vector STL y los contenedores de mapas, pero tienen algunas diferencias:

  • Estas colecciones no se pueden copiar ni asignar. (una vez que contienen objetos).
  • Los punteros a los objetos se insertan en ellos.
  • Cuando se elimina la colección, primero se llama al destructor en todos los objetos de la colección. (Tengo otra versión donde se afirma si se destruye y no está vacía).
  • Como almacenan punteros, también puede almacenar objetos heredados en estos contenedores.

Mi ventaja con STL es que está tan enfocado en los objetos Value mientras que en la mayoría de las aplicaciones los objetos son entidades únicas que no tienen una semántica de copia significativa requerida para usar en esos contenedores.


10

Bah, ustedes, niños pequeños, y sus nuevos recolectores de basura ...

Reglas muy estrictas sobre "propiedad": qué objeto o parte del software tiene derecho a eliminar el objeto. Comentarios claros y nombres sabios de variables para que sea obvio si un puntero "posee" o es "solo mira, no toques". Para ayudar a decidir quién posee qué, siga tanto como sea posible el patrón de "emparedado" dentro de cada subrutina o método.

create a thing
use that thing
destroy that thing

A veces es necesario crear y destruir en lugares muy diferentes; Creo que es difícil evitar eso.

En cualquier programa que requiera estructuras de datos complejas, creo un árbol estricto y claro de objetos que contienen otros objetos, utilizando punteros de "propietario". Este árbol modela la jerarquía básica de los conceptos de dominio de aplicación. Ejemplo, una escena 3D posee objetos, luces, texturas. Al final de la representación cuando el programa se cierra, hay una forma clara de destruir todo.

Muchos otros punteros se definen como necesarios cuando una entidad necesita acceder a otra, para escanear sobre arays o lo que sea; estos son los "solo mirando". Para el ejemplo de escena 3D: un objeto usa una textura pero no posee; otros objetos pueden usar esa misma textura. La destrucción de un objeto no invoca la destrucción de ninguna textura.

Sí, consume mucho tiempo, pero eso es lo que hago. Raramente tengo pérdidas de memoria u otros problemas. Pero luego trabajo en el ámbito limitado del software científico, de adquisición de datos y de gráficos de alto rendimiento. A menudo no trato transacciones como en la banca y el comercio electrónico, las GUI controladas por eventos o el caos asincrónico de alta red. ¡Quizás las formas novedosas tienen una ventaja allí!


Estoy totalmente de acuerdo Al trabajar en un entorno integrado, es posible que no tenga el lujo de las bibliotecas de terceros.
Simon

66
Estoy en desacuerdo. en la parte de "usar esa cosa", si se produce una devolución o una excepción, perderá la desasignación. En cuanto al rendimiento, std :: auto_ptr no le costaría nada. No es que nunca codifique de la misma manera que tú. Es solo que hay una diferencia entre el 100% y el 99% del código seguro. :-)
paercebal

8

Gran pregunta!

si está utilizando c ++ y está desarrollando una aplicación de CPU y memoria en tiempo real (como juegos), debe escribir su propio Administrador de memoria.

Creo que lo mejor que puedes hacer es fusionar algunos trabajos interesantes de varios autores, puedo darte una pista:

  • El asignador de tamaño fijo es ampliamente discutido, en todas partes en la red

  • La asignación de objetos pequeños fue presentada por Alexandrescu en 2001 en su libro perfecto "Diseño moderno de c ++"

  • Se puede encontrar un gran avance (con el código fuente distribuido) en un sorprendente artículo en Game Programming Gem 7 (2008) llamado "High Performance Heap allocator", escrito por Dimitar Lazarov

  • Puede encontrar una gran lista de recursos en este artículo

No empiece a escribir un novato asignador inútil por sí mismo ... DOCUMENTARSE primero.


5

Una técnica que se ha vuelto popular con la administración de memoria en C ++ es RAII . Básicamente utiliza constructores / destructores para manejar la asignación de recursos. Por supuesto, hay algunos otros detalles desagradables en C ++ debido a la seguridad de excepción, pero la idea básica es bastante simple.

El problema generalmente se reduce a uno de propiedad. Recomiendo leer la serie Effective C ++ de Scott Meyers y Modern C ++ Design de Andrei Alexandrescu.



4

¡Punteros inteligentes de usuario donde sea que puedas! Clases enteras de pérdidas de memoria simplemente desaparecen.


4

Comparta y conozca las reglas de propiedad de la memoria en su proyecto. El uso de las reglas COM proporciona la mejor consistencia (los parámetros [in] son ​​propiedad de la persona que llama, la persona que llama debe copiar; los parámetros [out] son ​​propiedad de la persona que llama, la persona que llama debe hacer una copia si mantiene una referencia; etc.)


4

valgrind es una buena herramienta para verificar las pérdidas de memoria de sus programas también en tiempo de ejecución.

Está disponible en la mayoría de los sabores de Linux (incluido Android) y en Darwin.

Si sueles escribir pruebas unitarias para tus programas, debes acostumbrarte a ejecutar sistemáticamente valgrind en las pruebas. Potencialmente evitará muchas pérdidas de memoria en una etapa temprana. También suele ser más fácil identificarlos en pruebas simples que en un software completo.

Por supuesto, este consejo es válido para cualquier otra herramienta de verificación de memoria.


3

Además, no use memoria asignada manualmente si hay una clase de biblioteca estándar (por ejemplo, vector). Asegúrate de violar esa regla que tienes un destructor virtual.


2

Si no puede / no usa un puntero inteligente para algo (aunque eso debería ser una gran bandera roja), escriba su código con:

allocate
if allocation succeeded:
{ //scope)
     deallocate()
}

Eso es obvio, pero asegúrese de escribirlo antes de escribir cualquier código en el alcance


2

Una fuente frecuente de estos errores es cuando tiene un método que acepta una referencia o puntero a un objeto pero deja en claro la propiedad. Las convenciones de estilo y comentarios pueden hacer que esto sea menos probable.

Deje que el caso donde la función toma posesión del objeto sea el caso especial. En todas las situaciones donde esto sucede, asegúrese de escribir un comentario al lado de la función en el archivo de encabezado que lo indique. Debe esforzarse por asegurarse de que, en la mayoría de los casos, el módulo o la clase que asigna un objeto también sea responsable de desasignarlo.

Usar const puede ayudar mucho en algunos casos. Si una función no modificará un objeto y no almacena una referencia que persiste después de que regrese, acepte una referencia constante. Al leer el código de la persona que llama, será obvio que su función no ha aceptado la propiedad del objeto. Podría haber tenido la misma función de aceptar un puntero sin constante, y la persona que llama puede haber asumido o no que la persona que llama aceptó la propiedad, pero con una referencia constante no hay duda.

No use referencias no constantes en las listas de argumentos. Al leer el código de la persona que llama, no está claro que la persona que llamó pudo haber mantenido una referencia al parámetro.

No estoy de acuerdo con los comentarios que recomiendan punteros contados de referencia. Esto generalmente funciona bien, pero cuando tiene un error y no funciona, especialmente si su destructor hace algo no trivial, como en un programa multiproceso. Definitivamente intente ajustar su diseño para que no necesite un recuento de referencias si no es demasiado difícil.


2

Consejos en orden de importancia:

-Tip # 1 Recuerde siempre declarar sus destructores "virtuales".

-Tip # 2 Use RAII

-Tip # 3 Usa los punteros inteligentes de boost

- Consejo # 4 No escriba sus propios Smartpointers con errores, use boost (en un proyecto en el que estoy ahora no puedo usar boost, y he sufrido la necesidad de depurar mis propios punteros inteligentes, definitivamente no tomaría la misma ruta nuevamente, pero de nuevo en este momento no puedo agregar impulso a nuestras dependencias)

-Consejo # 5 Si es algo casual / no crítico para el rendimiento (como en juegos con miles de objetos) funciona, mira el contenedor de puntero de impulso de Thorsten Ottosen

Consejo # 6 Encuentre un encabezado de detección de fugas para su plataforma de elección, como el encabezado "vld" de Visual Leak Detection


Puede que me falte un truco, pero ¿cómo pueden estar en la misma oración 'juego' y 'no crítico para el rendimiento'?
Adam Naylor

Los juegos son un ejemplo del escenario crítico, por supuesto. Podría haber fallado en ser claro allí
Robert Gould

El Consejo n. ° 1 solo debe aplicarse si la clase tiene al menos un método virtual. Nunca impondría un destructor virtual inútil en una clase que no está destinada a servir como una clase base en un árbol de herencia polimórfico.
antred

1

Si puede, use boost shared_ptr y C ++ auto_ptr estándar. Los que transmiten la semántica de propiedad.

Cuando devuelve un auto_ptr, le está diciendo a la persona que llama que le está dando la propiedad de la memoria.

Cuando devuelve un shared_ptr, le está diciendo a la persona que llama que tiene una referencia y que forma parte de la propiedad, pero no es responsabilidad exclusiva de ellos.

Esta semántica también se aplica a los parámetros. Si la persona que llama le pasa un auto_ptr, le están dando la propiedad.


1

Otros han mencionado formas de evitar pérdidas de memoria en primer lugar (como punteros inteligentes). Pero una herramienta de análisis de perfiles y memoria es a menudo la única forma de localizar problemas de memoria una vez que los tiene.

Valgrind memcheck es excelente y gratuito.


1

Solo para MSVC, agregue lo siguiente a la parte superior de cada archivo .cpp:

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

Luego, al depurar con VS2003 o superior, se le informará de cualquier fuga cuando su programa salga (rastrea nuevo / eliminar). Es básico, pero me ha ayudado en el pasado.



1

Si va a administrar su memoria manualmente, tiene dos casos:

  1. Creé el objeto (quizás indirectamente, llamando a una función que asigna un nuevo objeto), lo uso (o una función que llamo lo usa), luego lo libero.
  2. Alguien me dio la referencia, así que no debería liberarla.

Si necesita romper alguna de estas reglas, por favor documente.

Se trata de la propiedad del puntero.


1
  • Intente evitar asignar objetos dinámicamente. Mientras las clases tengan constructores y destructores apropiados, use una variable del tipo de clase, no un puntero, y evite la asignación dinámica y la desasignación porque el compilador lo hará por usted.
    En realidad, ese también es el mecanismo utilizado por los "punteros inteligentes" y algunos de los otros escritores lo denominan RAII ;-).
  • Cuando pase objetos a otras funciones, prefiera los parámetros de referencia a los punteros. Esto evita algunos posibles errores.
  • Declare los parámetros const, donde sea posible, especialmente los apuntadores a los objetos. De esa forma, los objetos no se pueden liberar "accidentalmente" (excepto si arroja la constante ;-))
  • Minimice el número de lugares en el programa donde realiza la asignación de memoria y la desasignación. P.ej. si asigna o libera el mismo tipo varias veces, escriba una función para él (o un método de fábrica ;-)).
    De esta manera, puede crear resultados de depuración (qué direcciones se asignan y desasignan, ...) fácilmente, si es necesario.
  • Use una función de fábrica para asignar objetos de varias clases relacionadas de una sola función.
  • Si sus clases tienen una clase base común con un destructor virtual, puede liberarlas utilizando la misma función (o método estático).
  • Verifique su programa con herramientas como purificar (desafortunadamente muchos $ / € / ...).

0

Puede interceptar las funciones de asignación de memoria y ver si hay algunas zonas de memoria no liberadas al salir del programa (aunque no es adecuado para todas las aplicaciones).

También se puede hacer en tiempo de compilación reemplazando los operadores new y delete y otras funciones de asignación de memoria.

Por ejemplo, consulte en este sitio [Depuración de la asignación de memoria en C ++] Nota: Hay un truco para eliminar el operador también algo como esto:

#define DEBUG_DELETE PrepareDelete(__LINE__,__FILE__); delete
#define delete DEBUG_DELETE

Puede almacenar en algunas variables el nombre del archivo y cuando el operador de eliminación sobrecargado sepa de dónde fue llamado. De esta manera puede tener el rastro de cada eliminación y malloc de su programa. Al final de la secuencia de comprobación de memoria, debería poder informar qué bloque de memoria asignado no se 'eliminó' identificándolo por nombre de archivo y número de línea, que supongo que es lo que desea.

También puede probar algo como BoundsChecker en Visual Studio, que es bastante interesante y fácil de usar.


0

Envolvemos todas nuestras funciones de asignación con una capa que agrega una breve cadena al frente y una bandera centinela al final. Entonces, por ejemplo, tendría una llamada a "myalloc (pszSomeString, iSize, iAlignment); o new (" description ", iSize) MyObject (); que asigna internamente el tamaño especificado más espacio suficiente para su encabezado y centinela. Por supuesto , no olvide comentar esto para compilaciones sin depuración. Se necesita un poco más de memoria para hacer esto, pero los beneficios superan con creces los costos.

Esto tiene tres beneficios: primero, le permite rastrear fácil y rápidamente qué código tiene fugas, haciendo búsquedas rápidas para el código asignado en ciertas 'zonas' pero no limpiado cuando esas zonas deberían haberse liberado. También puede ser útil detectar cuándo se ha sobrescrito un límite verificando que todos los centinelas estén intactos. Esto nos ha salvado numerosas veces al tratar de encontrar esos bloqueos bien ocultos o pasos en falso de la matriz. El tercer beneficio es rastrear el uso de la memoria para ver quiénes son los grandes jugadores: una recopilación de ciertas descripciones en un MemDump te dice cuándo, por ejemplo, el "sonido" ocupa mucho más espacio de lo que esperabas.


0

C ++ está diseñado RAII en mente. Realmente no hay mejor manera de administrar la memoria en C ++, creo. Pero tenga cuidado de no asignar fragmentos muy grandes (como objetos de búfer) en el ámbito local. Puede causar desbordamientos de pila y, si hay una falla en la comprobación de límites al usar ese fragmento, puede sobrescribir otras variables o direcciones de retorno, lo que conduce a agujeros de seguridad de todo tipo.


0

Uno de los únicos ejemplos sobre la asignación y destrucción en diferentes lugares es la creación de subprocesos (el parámetro que pasa). Pero incluso en este caso es fácil. Aquí está la función / método que crea un hilo:

struct myparams {
int x;
std::vector<double> z;
}

std::auto_ptr<myparams> param(new myparams(x, ...));
// Release the ownership in case thread creation is successfull
if (0 == pthread_create(&th, NULL, th_func, param.get()) param.release();
...

Aquí, en cambio, la función de hilo

extern "C" void* th_func(void* p) {
   try {
       std::auto_ptr<myparams> param((myparams*)p);
       ...
   } catch(...) {
   }
   return 0;
}

Bastante fácil, ¿no? En caso de que la creación del hilo falle, el recurso será liberado (eliminado) por el auto_ptr; de lo contrario, la propiedad pasará al hilo. ¿Qué pasa si el hilo es tan rápido que después de la creación libera el recurso antes de

param.release();

se llama en la función / método principal? ¡Nada! Porque 'le diremos' al auto_ptr que ignore la desasignación. ¿La administración de memoria C ++ es fácil, no? Salud,

Ema!


0

Administre la memoria de la misma manera que administra otros recursos (identificadores, archivos, conexiones db, sockets ...). GC tampoco te ayudaría con ellos.


-3

Exactamente un retorno de cualquier función. De esa manera, puede hacer la desasignación allí y nunca perderse.

De lo contrario, es muy fácil cometer un error:

new a()
if (Bad()) {delete a; return;}
new b()
if (Bad()) {delete a; delete b; return;}
... // etc.

¿Su respuesta no coincide con el código de ejemplo aquí? Estoy de acuerdo con la respuesta "solo una devolución" pero el código de ejemplo muestra lo que NO debe hacer.
simon

1
El objetivo de C ++ RAII es exactamente evitar el tipo de código que escribió. En C, esto es probablemente lo correcto. Pero en C ++, su código es defectuoso. Por ejemplo: ¿Qué pasa si arroja nuevo b ()? Tu fuga a.
paercebal
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.