Análisis detallado 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 se destruye un objeto. Su función principal es liberar los recursos que el objeto adquirió durante su vida útil, como memoria asignada dinámicamente, identificadores 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 en estas situaciones:
- Los objetos de la pila salen de ámbito
- Se llama a
deletesobre un objeto asignado en el heap - Finaliza la vida útil 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 de f2
} // Se llama al destructor de f1 (sale del ámbito)A diferencia de los constructores, una clase solo puede tener un destructor. No puedes sobrecargarlo.
Si no defines uno, el compilador genera un destructor predeterminado que no hace nada especial: simplemente destruye cada miembro. Para las clases que administran recursos, debes escribir el tuyo para evitar fugas de memoria.
Desafío
FácilVamos a crear un administrador de sesiones que registre las sesiones de usuario activas y demuestre cómo los destructores limpian automáticamente los recursos cuando se destruyen los objetos, ya sea al salir del ámbito o al eliminarlos explícitamente.
Crearás dos archivos para organizar tu código:
Session.h: Define una claseSessionque representa 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 registre 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 el tamaño del 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 la siguiente posición disponible (lleva la cuenta interna de la posición actual); si el registro ya está lleno, se ignora la llamada, por lo que el recuento nunca supera el tamaño del registro - 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 de los destructores en distintos escenarios. Lee un nombre de usuario y el tamaño del registro desde la entrada (cada uno en una línea separada) y luego:- Crea un bloque de ámbito con llaves
{ }y dentro crea unaSessionasignada en la pila con el nombre de usuario de la entrada, el ID de sesión101y el tamaño del registro de la entrada. Registra dos actividades con los códigos1y2, y luego imprime"Stack session activities: <count>". Cuando termina el bloque, 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 el tamaño del registro3. Registra una actividad con el código5, imprime"Heap session activities: <count>"y luego elimínala explícitamente condelete. - Después de eliminarla, imprime
"Heap session destroyed"
- Crea un bloque de ámbito con llaves
Este desafío muestra las dos formas principales en que se llaman los destructores: automáticamente cuando los objetos de la pila salen del ámbito y manualmente cuando eliminas objetos del heap. Ambas vías deben liberar correctamente el registro de actividad asignado dinámicamente para evitar fugas de memoria.
Incluye tu archivo de encabezado en main.cpp usando #include "Session.h".
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// Lee la entrada
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO: Crea un bloque de ámbito con llaves { }
// Dentro del bloque:
// - Crea una Session en la pila con el nombre de usuario de entrada, el ID de sesión 101 y el tamaño de registro de entrada
// - Registra dos actividades con los códigos 1 y 2
// - Imprime "Stack session activities: <count>"
// Cuando termina el bloque, el destructor se ejecuta automáticamente
// TODO: Después del bloque, imprime "Stack session destroyed"
// TODO: Crea una Session en el montón usando new
// - Nombre de usuario: "Guest", ID de sesión: 202, tamaño de registro: 3
// - Registra una actividad con el código 5
// - Imprime "Heap session activities: <count>"
// - Elimina la sesión
// TODO: Después de eliminarla, imprime "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 la POO
Archivos externosCompilación y construcción en C++Archivos de cabecera y archivos fuenteEspacios de nombres y ámbitoIntroducción a la POO en C++Clases y objetosEl puntero 'this'Métodos (funciones miembro)Atributos (miembros de datos)Fundamentos de constructores y destructoresRepaso: calculadora sencilla4Propiedades de clase
Miembros de instancia vs. estáticosGetters y settersFunciones miembro constPalabra clave mutableMétodos y variables estáticosFunciones y clases friendRepaso: gestor de cuentas bancarias7Herencia
Herencia básicaNiveles de acceso en la herenciaOrden de llamada de constructores y destructoresSobrescritura de métodosFunciones virtuales y VTableHerencia múltipleHerencia virtualRepaso: jerarquía de empleados2Gestión de memoria
Memoria de pila vs. heapPunteros y referenciasMemoria dinámica (new/delete)Punteros inteligentes en C++RAII en C++Repaso: gestor de arreglos dinámicos5Encapsulación
Especificadores de acceso en C++Especificadores de acceso en profundidadOcultación de informaciónStruct vs. classClases anidadas e internasRepaso: sistema de registros de estudiantes8Polimorfismo
Polimorfismo en compilación y en tiempo de ejecuciónSobrecarga de funcionesRepaso de las funciones virtualesFunciones virtuales purasClases abstractasDiseño de interfaces en C++Conversión dinámica y RTTIRepaso: calculadora de formas11Conceptos avanzados de POO
Composición frente a herenciaMixins mediante CRTPIdiom PimplBorrado de tiposClases enum 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 predeterminadoConstructor con parámetrosConstructor de copiaConstructor de movimientoListas de inicialización del constructorConstructores delegadosAnálisis detallado del destructorRegla de tres / cinco / ceroRepaso: clase String6Sobrecarga de operadores
Introducción a la sobrecarga de operadoresSobrecarga de operadores aritméticosSobrecarga de operadores de comparaciónOperadores de flujoSobrecarga del operador de asignaciónSobrecarga de los operadores [] y ()Operadores de conversión de tipoRepaso: clase Matrix9Plantillas
Plantillas de funcionesPlantillas de clasesEspecialización de plantillasPlantillas variádicasFundamentos de SFINAE y rasgos de tiposRepaso: contenedor genéricoPractica por tu cuenta: Compilador de C++ online