¿Se llama a un destructor cuando un objeto sale del alcance?


Por ejemplo:

int main() {
    Foo *leedle = new Foo();

    return 0;
}

class Foo {
private:
    somePointer* bar;

public:
    Foo();
    ~Foo();
};

Foo::~Foo() {
    delete bar;
}

¿El compilador llamaría implícitamente al destructor o habría una fuga de memoria?

Soy nuevo en la memoria dinámica, así que si este no es un caso de prueba utilizable, lo siento.

Author: Tux, 2013-07-17

5 answers

Sí, las variables automáticas se destruirán al final del bloque de código adjunto. Pero sigue leyendo.

El título de la pregunta pregunta si se llamará a un destructor cuando la variable salga del ámbito. Presumiblemente lo que querías preguntar era:

¿Se llamará al destructor de Foo al final de main()?

Dado el código que proporcionó, la respuesta a esa pregunta es no ya que el objeto Foo tiene una duración de almacenamiento dinámica, como veremos breve.

Observe aquí qué es la variable automática:

Foo* leedle = new Foo();

Aquí, leedle es la variable automática que será destruida. leedle es solo un puntero. La cosa a la que leedle apunta no tiene una duración de almacenamiento automática, y no será destruida. Entonces, si haces esto:

void DoIt()
{
  Foo* leedle = new leedle;
}

Filtras la memoria asignada por new leedle.


Usted debe delete cualquier cosa que se haya asignado con new:

void DoIt()
{
  Foo* leedle = new leedle;
  delete leedle;
}

Esto es hecho mucho más simple y más robusto mediante el uso de punteros inteligentes. En C++03:

void DoIt()
{
  std::auto_ptr <Foo> leedle (new Foo);
}

O en C++11:

void DoIt()
{
  std::unique_ptr <Foo> leedle = std::make_unique <Foo> ();
}

Los punteros inteligentes se usan como variables automáticas, como anteriormente, y cuando salen del alcance y se destruyen, automáticamente (en el destructor) delete el objeto al que se apunta. Así que en ambos casos anteriores, no hay pérdida de memoria.


Tratemos de aclarar un poco el lenguaje aquí. En C++, las variables tienen una duración de almacenamiento. En C++03, hay 3 almacenamiento duraciones:

1: automático: Una variable con duración de almacenamiento automática será destruida al final del bloque de código que encierra.

Considere:

void Foo()
{
  bool b = true;
  {
    int n = 42;
  } // LINE 1
  double d = 3.14;
} // LINE 2

En este ejemplo, todas las variables tienen una duración de almacenamiento automática. Tanto b como d serán destruidos en la LÍNEA 2. n será destruido en LA LÍNEA 1.

2: static: Se asignará una variable con duración de almacenamiento estático antes de que comience el programa, y se destruirá cuando el programa termina.

3: dynamic: Una variable con duración de almacenamiento dinámica se asignará cuando la asigne usando funciones de asignación de memoria dinámica (por ejemplo, new) y se destruirá cuando la destruya usando funciones de asignación de memoria dinámica (por ejemplo, delete).

En mi ejemplo original de arriba:

void DoIt()
{
  Foo* leedle = new leedle;
}

leedle es una variable con duración de almacenamiento automática y se destruirá al final de la llave. La cosa a la que leedle apunta tiene almacenamiento dinámico duración y no se destruye en el código anterior. Debes llamar a delete para desasignarlo.

C++11 también agrega una cuarta duración de almacenamiento:

4: thread : Las variables con duración de almacenamiento de subprocesos se asignan cuando comienza el subproceso y se desasignan cuando termina el subproceso.

 83
Author: John Dibling,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2015-03-23 18:45:59

Sí, si un objeto sale del ámbito, se llama al destructor. PERO No, el destructor no será llamado en este caso, porque solo tiene un puntero en el alcance, ese puntero no tiene un destructor en particular, por lo que no habrá una llamada indirecta al destructor de Foo.

Este ejemplo es el dominio de aplicación de punteros inteligentes como std::unique_ptr y std::shared_ptr. Esas son clases reales que, a diferencia de los punteros crudos tienen un destructor, llamando (condicionalmente) a delete en el objeto apuntado.

Btw, Foo's destructor deletes bar, bur bar nunca ha sido inicializado ni asignado a una dirección que apunta a un objeto real, por lo que la llamada delete dará un comportamiento indefinido, probablemente un bloqueo.

 6
Author: Arne Mertz,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-17 14:54:33

Habría una fuga de memoria de hecho. El destructor para el objeto que sale del ámbito (el Foo*) es llamado, pero el del objeto apuntado (el Foo que usted asignó) no lo hace.

Técnicamente hablando, ya que usted está en la principal, no es una fuga de memoria, ya que hasta cuando la aplicación no se termina se puede acceder a todas las variables asignadas. Con este respecto, cito Alexandrescu (de C++ moderno, el capítulo sobre singletons)

Fugas de memoria aparece cuando asigna la acumulación de datos y pierde todo referencias a ella. Este no es el caso aquí: Nada se acumula, y tenemos conocimiento sobre la memoria asignada hasta el final de la aplicación. Además, todos los

Por supuesto, esto no implica que no deba llamar a delete, ya que sería una práctica extremadamente mala (y peligrosa).

 2
Author: Stefano Falasca,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-17 14:54:13

Primero tenga en cuenta que el código no se compilaría; new devuelve un puntero a un objeto asignado en el montón. Necesitas:

int main() {
    Foo *leedle = new Foo();
    return 0;
}

Ahora, dado que new asigna el objeto con almacenamiento dinámico en lugar de automático, no está fuera de alcance al final de la función. Por lo tanto, tampoco se va a eliminar, y se ha filtrado la memoria.

 1
Author: Joni,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-17 15:02:57

En este caso, cuando main devuelve que es el final del programa, el sistema operativo se encargará de liberar todos los recursos. Si, por ejemplo, esta fuera cualquier otra función, tendrías que usa borrar.

 -5
Author: Scotty Bauer,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/ajaxhispano.com/template/agent.layouts/content.php on line 61
2013-07-17 14:49:02