Análisis profundo del destructor
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 24 de 104.
Un destructor es una función miembro especial que se ejecuta automáticamente cuando un objeto es destruido. Su tarea principal es liberar los recursos que el objeto adquirió durante su tiempo de vida, como la memoria asignada dinámicamente, los identificadores de archivos o las conexiones de red.
El destructor tiene el mismo nombre que la clase, prefijado con una tilde ~. No toma parámetros y no tiene tipo de retorno:
class FileHandler {
std::string filename;
int* buffer;
public:
FileHandler(std::string name, size_t size)
: filename(name), buffer(new int[size]) {
std::cout << "Opening " << filename << "\n";
}
~FileHandler() {
delete[] buffer;
std::cout << "Closing " << filename << "\n";
}
};Los destructores se llaman automáticamente en estas situaciones:
- Los objetos de la pila salen de su ámbito
- Se llama a
deletepara un objeto asignado en el montículo - El tiempo de vida de un objeto temporal termina
void example() {
FileHandler f1("data.txt", 100); // Se llama al constructor
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Se llama al destructor para f2
} // Se llama al destructor para f1 (sale del ámbito)A diferencia de los constructores, una clase solo puede tener un destructor. No se puede sobrecargar.
Si no defines uno, el compilador genera un destructor predeterminado que no hace nada especial: simplemente destruye cada miembro. Para las clases que gestionan recursos, debes escribir el tuyo propio para evitar fugas de memoria.
Desafío
FácilVamos a construir un gestor de sesiones que rastree las sesiones de usuario activas y demuestre cómo los destructores limpian automáticamente los recursos cuando los objetos se destruyen, ya sea porque salen de su ámbito o porque se eliminan explícitamente.
Crearás dos archivos para organizar tu código:
Session.h: Define una claseSessionque represente una sesión de usuario activa con datos asignados dinámicamente. Tu clase debe tener:- Miembros privados: un
username(string), unsessionId(int), y un puntero a un arreglo de enteros llamadoactivityLogque rastrea las acciones del usuario, junto con unlogSizepara el tamaño del arreglo. - Un constructor que reciba un nombre de usuario, un ID de sesión y un tamaño de registro. Debe asignar el arreglo del registro de actividad en el heap, inicializar todos los elementos a 0 e imprimir
"Session <sessionId> started for <username>". - Un destructor que libere la memoria asignada e imprima
"Session <sessionId> ended for <username>". - Un método
logActivity(int activityCode)que almacene el código de actividad en el siguiente espacio disponible (rastrea la posición actual internamente). - Un método
getActivityCount()que devuelva cuántas actividades se han registrado. - Un método
getUsername()que devuelva el nombre de usuario.
- Miembros privados: un
main.cpp: Demuestra el comportamiento del destructor en diferentes escenarios. Lee un nombre de usuario y un tamaño de registro de la entrada (cada uno en una línea separada), luego:- Crea un bloque de ámbito usando llaves
{ }y dentro de él crea unaSessionasignada en la pila (stack) con el nombre de usuario de entrada, el ID de sesión101y el tamaño de registro de entrada. Registra dos actividades con los códigos1y2, luego imprime"Stack session activities: <count>". Cuando el bloque termine, el destructor debe ejecutarse automáticamente. - Después del bloque, imprime
"Stack session destroyed". - Crea una
Sessionasignada en el heap usandonewcon el nombre de usuario"Guest", el ID de sesión202y un tamaño de registro de3. Registra una actividad con el código5, imprime"Heap session activities: <count>", y luego elimínala explícitamente condelete. - Después de la eliminación, imprime
"Heap session destroyed".
- Crea un bloque de ámbito usando llaves
Este desafío muestra las dos formas principales en que se llaman a los destructores: automáticamente cuando los objetos de la pila salen de su ámbito, y manualmente cuando eliminas objetos del heap. Ambos caminos deben liberar adecuadamente el registro de actividad asignado dinámicamente para evitar fugas de memoria.
Incluye tu archivo de cabecera en main.cpp usando #include "Session.h".
Hoja de referencia
Un destructor es una función miembro especial que se ejecuta automáticamente cuando un objeto es destruido. Libera recursos como memoria asignada dinámicamente, manejadores de archivos o conexiones de red.
El destructor tiene el mismo nombre que la clase, precedido por una tilde ~. No recibe parámetros y no tiene tipo de retorno:
class FileHandler {
std::string filename;
int* buffer;
public:
FileHandler(std::string name, size_t size)
: filename(name), buffer(new int[size]) {
std::cout << "Opening " << filename << "\n";
}
~FileHandler() {
delete[] buffer;
std::cout << "Closing " << filename << "\n";
}
};Los destructores se llaman automáticamente cuando:
- Los objetos de pila (stack) salen de su ámbito
- Se llama a
deleteen un objeto asignado en el montículo (heap) - Termina el tiempo de vida de un objeto temporal
void example() {
FileHandler f1("data.txt", 100); // Se llama al constructor
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Se llama al destructor para f2
} // Se llama al destructor para f1 (sale del ámbito)Una clase solo puede tener un destructor (sin sobrecarga). Si no defines uno, el compilador genera un destructor por defecto. Para las clases que gestionan recursos, debes escribir el tuyo propio para evitar fugas de memoria.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// Leer entrada
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO: Crear un bloque de alcance con llaves { }
// Dentro del bloque:
// - Crear una Session asignada en la pila con el username de entrada, el ID de sesión 101 y el tamaño de registro de entrada
// - Registrar dos actividades con los códigos 1 y 2
// - Imprimir "Stack session activities: <count>"
// Cuando el bloque termina, el destructor se ejecuta automáticamente
// TODO: Después del bloque, imprimir "Stack session destroyed"
// TODO: Crear una Session asignada en el montículo usando new
// - Username: "Guest", ID de sesión: 202, tamaño de registro: 3
// - Registrar una actividad con el código 5
// - Imprimir "Heap session activities: <count>"
// - Eliminar la sesión
// TODO: Después de la eliminación, imprimir "Heap session destroyed"
return 0;
}
Esta lección incluye un breve cuestionario. Empieza la lección para responderlo y registrar tu progreso.
Todas las lecciones de Programación Orientada a Objetos
1Fundamentos de OOP
Archivos externosConstrucción y compilación en C++Archivos de cabecera y archivos fuenteNamespaces y alcanceIntroducción a OOP en C++Clases vs ObjetosEl puntero 'this'Métodos (Funciones miembro)Atributos (Miembros de datos)Conceptos básicos de Ctors y DtorsResumen - Calculadora simple4Propiedades de clase
Miembros de instancia vs. estáticosGetters y SettersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases amigasResumen - Gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de Ctor y DtorSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualResumen - Jerarquía de empleados2Gestión de memoria
Memoria Stack vs HeapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Resumen - Gestor de arrays dinámicos5Encapsulamiento
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultamiento de informaciónStruct vs ClassClases anidadas e internasResumen - Sistema de registros de estudiantes8Polimorfismo
Polimorfismo: Compilación vs. Tiempo de ejecuciónSobrecarga de funcionesFunciones virtuales revisadasFunciones virtuales purasClases abstractasDiseño de interfaces en C++Dynamic Casting y RTTIResumen: Calculadora de figuras11Conceptos avanzados de POO
Composición vs. HerenciaMixins mediante CRTPIdioma PimplBorrado de tiposEnum Classes y tipado fuerteManejo de excepciones en POOJerarquías de excepciones personalizadas14Patrones de diseño - Parte 2
Patrón CommandPatrón AdapterPatrón DecoratorPatrón Template MethodPatrón StatePatrón CompositeRAII como patrón3Constructores y Destructores
Constructor por defectoConstructor parametrizadoConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis profundo del destructorRegla de tres / cinco / ceroResumen - Clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujo (Stream)Sobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tiposResumen - Clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasConceptos básicos de SFINAE y Type TraitsResumen - Contenedor genérico