Dudas con destructor

Supongamos que tengo una clase:

class ClassX{
   private:
      int* p;
      int i;
   ....
}

Y en algún lugar hago:

ClassX x;
.....
//and in a friend function or in a ClassX function
p = (int*) malloc (.....);

Entonces cuando x salir de su alcance una vez que se invoque el destructor; pero no libera la memoria asignada por el malloc ¿derecho?

Y si lo redefino:

ClassX::~ClassX(){ delete [] p; }

libera la memoria asignada por el malloc pero no la memoria asignada para los campos de la clase (es decir, i y p)?

¿Me estoy perdiendo algo?

Gracias por su atención.

preguntado el 03 de mayo de 12 a las 15:05

Haga coincidir malloc() con free() y new[] con delete[] -

Mejor aún, no use malloc/free en código C++. Y aprenda sobre RAII y punteros inteligentes lo antes posible. -

6 Respuestas

En primer lugar, recuerda estas cosas:

  1. Cosas asignadas por malloc necesita ser desasignado por free
  2. No use malloc
  3. Cosas asignadas por new debe ser desasignado por delete
  4. Cosas asignadas por new[] debe ser desasignado por delete[]
  5. Una delete para cada new y uno delete[] para cada new[]
  6. No uses ninguno de los anteriores.

Entonces, tiene razón al pensar que, sin un destructor, la memoria asignada por malloc no será liberado. Si sigue las reglas anteriores, entonces necesita usar free (no delete[]) para desasignarlo:

ClassX::~ClassX() { free(p); }

Sin embargo, no deberías usar malloc en C ++ en primer lugar, ya que no llama a los constructores de objetos, debe usar new:

ClassX::ClassX() : p(new int) { }

// NOW we use delete since we used new (not delete[] since we didn't use new[])
ClassX::~ClassX() { delete p; }

Sin embargo, si hace eso, debe escribir un constructor de copia, un operador de asignación de copia, un constructor de movimiento y un operador de asignación de movimiento. Así que echemos un vistazo a una manera aún mejor:

class ClassX{
private:
    ClassX();

    std::unique_ptr<int> p;
    int i;
    ....
};

// we have to break rule #6 here
ClassX::ClassX() : p(new int) { }

Ahora ni siquiera tiene que escribir un destructor, puede dejar que el puntero inteligente se ocupe de él porque llamará automáticamente delete en lo que hiciste con new cuando se llama a su destructor. Lo que nos lleva a...

Tu otra pregunta:

¿libera la memoria asignada por el malloc pero no la memoria asignada para los campos de la clase (es decir, iyp)?

Eso es aproximadamente 1/4 correcto. Como i y p son miembros de la clase, se desasignan automáticamente cuando se desasigna la clase envolvente y, si fueran clases en sí mismas, se llamaría a sus destructores. Así que si pones delete en su destructor, que se encarga de la memoria asignada por newy i y p se limpian automáticamente y todo está bien. (Solo acertó 1/4 porque usó la función de desasignación incorrecta).

Esto significa que, si usa un puntero inteligente, se llamará al destructor del puntero inteligente cuando se destruya su objeto, y desasignará automáticamente lo que asignó con new para ti. Así que ni siquiera tienes que preocuparte por eso.

Todo esto es asumiendo que Realmente quiero tener una dinamica int como parte de tu clase. Sin embargo, si puede, preferiría guardar el int por valor (no almacenar un puntero en él) y de esa manera no tiene que meterse con punteros inteligentes, desasignación o cualquier otra cosa.

contestado el 03 de mayo de 12 a las 16:05

Gracias por tu clara respuesta. Tengo otra pregunta: usted escribió "se desasignan automáticamente cuando se desasigna la clase adjunta". Entonces, la desasignación de la clase envolvente no la realiza el destructor de clases, ¿verdad? - aslan986

@ Aslan986 eso es absolutamente correcto. Lo hace la persona que llama; si lo asignaron con new, ellos debe desasignarlo con delete. Si lo asignaron en la pila, el compilador lo desasigna automáticamente cuando sale del alcance. El destructor de la clase solo está destinado a desasignar cosas que se asignaron dinámicamente (con new or new[]). Y después de llamar al destructor de la clase, se llaman los destructores de todos los miembros. Luego, toda la clase se desasigna (como dije anteriormente, con delete o automáticamente por el compilador). Y todo eso se hace automáticamente. - Seth Carnegie

La regla simple es: por cada malloc debe haber exactamente uno free; para cada new, debe haber exactamente uno delete; para cada new[] debe haber exactamente uno delete[].

Para responder a sus preguntas:

[el destructor] no libera la memoria asignada por el malloc, ¿verdad?

Correcto. No es asi. En ese caso, ha invocado malloc, pero nunca free.

[el destructor modificado] libera la memoria asignada por el malloc pero no la memoria asignada para los campos de la clase?

Incorrecto. No libera la memoria (debes usar freeno, delete[] en este caso. free is mallocsocio de s. delete[] is new[]socio de No puedes cambiar de pareja.)

También es incorrecto, libera la memoria asignada para los campos de la clase (suponiendo que los campos no sean tipos de puntero). Se libera la memoria ocupada por un objeto. después de el destructor regresa.

¿Me estoy perdiendo algo?

Si va a utilizar punteros en bruto, también necesita comprender el Regla de tres. Pero, mejor aún, nunca uses punteros en bruto. Utilizar una puntero inteligente o con una envase.

contestado el 23 de mayo de 17 a las 13:05

Tiene razón en que su destructor debería liberar p, pero si asigna con malloc deberías liberar la memoria con free. Alternativamente, puede asignar la memoria con:

x.p = new int[size];

Esto significaría que su delete[] p; destructor sería correcto.

Con respecto a las variables miembro de la clase, no necesita preocuparse por eliminarlas, si su objeto se asignó en la pila, se eliminarán cuando la pila se relaje. Si su clase se asignó en el montón, se eliminarán cuando delete x;. De cualquier manera, su tiempo de vida está vinculado al objeto que los contiene.

contestado el 03 de mayo de 12 a las 16:05

Gracias por tu respuesta. Pero no puedo entender qué hace el destructor "por defecto". Si no lo redefino, ¿simplemente no hace nada? - aslan986

Correcto, por defecto tienes un destructor vacío que no hace nada. Pero eso no significa que sus variables miembro no se eliminarán. - Benj

Las reglas son simples:

  • Una memoria asignada con new debe ser liberado con delete.
  • Una memoria asignada con new [] debe ser liberado con delete [].
  • Una memoria asignada con malloc() debe ser liberado con free().

Tenga en cuenta que debe pasar exactamente la misma dirección devuelta por las funciones de asignación a las funciones de desasignación.

Buenas practicas:

  • Siempre es una buena práctica evitar las asignaciones dinámicas a menos que realmente las necesite.
  • Si es necesario, debe usar algún tipo de puntero inteligente que se adapte a sus requisitos, la decisión de qué puntero inteligente usar depende de la propiedad y la semántica de por vida involucrada.
  • En C ++, es raro que uno necesite usar malloc y free y no los use a menos que tenga buenas razones para hacerlo,

Además, en su caso, también debe seguir las Regla de tres para que su programa funcione correctamente.

contestado el 23 de mayo de 17 a las 13:05

Si asigna memoria manualmente, debe liberarla, no se libera automáticamente para usted.

Si llamas malloc (o calloc o el realloc) necesitas free ese recuerdo Del mismo modo, si Ud. new algo delete eso y new[] debe coincidir con delete[].

Dicho esto, está programando en C++ y rara vez hay una buena razón para administrar la memoria manualmente. Hacer uso de punteros inteligentes, std :: shared_ptr para propiedad compartida y std :: unique_ptr de otra manera.

Entonces tu clase se ve así:

#include <memory>

class ClassX{
   private:
      std::unique_ptr<int> p;
      int i;
      ....
};

ClassX::ClassX() : p( new int ) {} /* No need to deallocate p manually */

Tenga en cuenta que unique_ptr también tiene una especialización parcial para arreglos, así que si p apunta a una matriz, puede asignar memoria de la siguiente manera:

class ClassX{
   private:
      std::unique_ptr<int[]> p;
      int i;
      ....
};

Class::ClassX() : p ( new int[10] ) {}

También puede olvidarse por completo de tratar con punteros y usar std::vector or std::array, especialmente para algo simple como una matriz de enteros.

contestado el 03 de mayo de 12 a las 16:05

Sin embargo, si usa new[] con unique_ptr, el tipo debe ser std::unique_ptr<int[]>no, std::unique_ptr<int>. - Seth Carnegie

Para simplificar, siempre me digo a mí mismo que un puntero ES solo un número entero. (puede ser de 16, 32 o 64 bits dependiendo del sistema). Es un entero especial que ayuda a definir una dirección, pero es solo un numero entero.

Entonces, cuando construyes la clase X, asigna el espacio para dos 'enteros'. Uno llamado p, el otro llamado i. Y cuando se destruye la clase desasigna el espacio de dos enteros y algunas cosas.

Al destructor no le importa lo que hayas hecho con el valor del entero especial p (que representa una dirección). Podrías haber creado memoria como lo hiciste, o crear un número aleatorio: todo es lo mismo en la memoria que representa la clase X: solo un número entero.

contestado el 03 de mayo de 12 a las 16:05

No es la respuesta que estás buscando? Examinar otras preguntas etiquetadas or haz tu propia pregunta.