Excepciones, destructores y técnica RAII (II)

Duración de almacenamiento

La siguiente tabla indica, a modo de recordatorio, el tiempo de vida de una variable en virtud de su tipo de duración de almacenamiento:

Duración de almacenamiento Tipo de variable afectada Finalización del tiempo de vida
Estática (i) Declarada en un ámbito namespace (incluido el espacio de nombres global) y/o (ii) declarada static o extern. Al terminar la ejecución del programa.
Automática Local, excepto si se declara static, extern o thread_local. Cuando el flujo del programa abandona el ámbito donde la variable fue creada. En ese momento, los objetos con almacenamiento automático (variables locales) creados en dicho ámbito son destruidos en orden inverso a su construcción.
Dinámica Alojada dinámicamente durante la ejecución del programa mediante una expresión de tipo new. Al alcanzarse una expresión de tipo delete.
Thread-local Declarada thead_local. Al finalizar la ejecución del hilo donde la variable fue creada.

A modo de ejemplo puramente ilustrativo, consideremos el siguiente código:

   #include <iostream>    struct X {       int i;       X(int ii) : i{ii} { std::cout << "Construct X object #" << i << '\n'; }       ~X() { std::cout << "Destruct X object #" << i << '\n'; }    };    auto main() -> int    {       auto a = X{1};          // (A)       auto b = X{2};          // (B)       X* p = new X{3};        // (C)       static auto c = X{4};   // (D)       // ...       delete p;               // (E)       std::cout << "Exiting main function\n";    } // (F)    /* Output:       "Construct X object #1" from (A)       "Construct X object #2" from (B)       "Construct X object #3" from (C)       "Construct X object #4" from (D)       "Destruct X object #3"  from (E)       "Exiting main function"       "Destruct X object #2" and...       "Destruct X object #1"  from (F)       "Destruct X object #4" al final de la ejecución del programa    */

Las variables locales ab (de tipo X) y p (de tipo X*) son inicializadas en (A), (B) y (C), respectivamente, poseyendo todas ellas una duración de almacenamiento automática. El objeto referenciado por el puntero p posee una duración de almacenamiento dinámica, siendo destruido y su memoria liberada en (E) mediante una expresión de tipo delete. El almacenamiento del puntero p, sin embargo, aún prosigue tras (E), por lo que podría ser reasignado con la dirección en memoria de otro objeto del tipo apropiado. El tiempo de vida de ab y p acaba en (F), al salir éstos fuera del ámbito donde fueron creados (en este caso, la función principal main). Es en dicho punto cuando los objetos a y b son destruidos en orden inverso a su creación invocándose automáticamente al destructor de la clase X. Respecto al objeto c de tipo X creado en (D), éste posee duración de almacenamiento estática, siendo destruido al finalizar la ejecución del programa.

Como regla general, y salvo en situaciones muy específicas, nunca debe llamarse explícitamente al destructor de una variable local. Para controlar su tiempo de vida, basta encerrarla en un ámbito adecuado (definido por un par cerrado de llaves {}):

   {       auto x1 = X{1};       {          auto x2 = X{2};          // ...       } // x2 es destruido aquí              // ...    } // x1 es destruido aquí


Puedes acceder al siguiente artículo de la serie a través de este enlace.

No hay comentarios:

Publicar un comentario