Constructores y destructores en C++
Aprende constructores y destructores en C++ para comprender el ciclo de vida de los objetos y la gestión de recursos.
En C++, cada clase tiene métodos especiales que se llaman cuando se crea o destruye un objeto: el constructor y el destructor. El constructor prepara el objeto para su uso inicial, mientras que el destructor libera la memoria. Esta estructura constituye la base del principio RAII (Resource Acquisition Is Initialization).
1. ¿Qué es un Constructor?
Un constructor es un método especial que se llama automáticamente cuando se crea un objeto de una clase. Su propósito es inicializar las variables y preparar los recursos necesarios.
- Su nombre debe ser igual al de la clase.
- No tiene tipo de retorno.
- Puede sobrecargarse (varios constructores en la misma clase).
class Book {
private:
string title;
int pages;
public:
// Constructor por defecto
Book() {
title = "Desconocido";
pages = 0;
cout << "Constructor por defecto llamado." << endl;
}
// Constructor con parámetros
Book(string t, int p) {
title = t;
pages = p;
cout << "Constructor con parámetros llamado." << endl;
}
void Display() {
cout << "Libro: " << title << " (" << pages << " páginas)" << endl;
}
};
El constructor se ejecuta automáticamente cuando se crea el objeto:
int main() {
Book b1;
Book b2("Programación en C++", 900);
b1.Display();
b2.Display();
}
2. ¿Qué es un Destructor?
Un destructor es un método especial que se llama cuando la vida útil de un objeto termina. Se utiliza para realizar operaciones de limpieza, como liberar memoria o cerrar archivos.
- Su nombre es igual al de la clase, pero comienza con
~(tilde). - No acepta parámetros y no puede sobrecargarse.
class Book {
public:
Book() {
cout << "Constructor ejecutado." << endl;
}
~Book() {
cout << "Destructor ejecutado." << endl;
}
};
int main() {
Book b;
} // El destructor se llama automáticamente al final de main()
Salida:
Constructor ejecutado.
Destructor ejecutado.
Dado que el destructor se llama automáticamente, no debe invocarse manualmente.
3. Ejemplo Book: Constructor y Destructor Avanzados
Ahora hagamos la clase Book más realista. Contendrá datos asignados dinámicamente (por ejemplo, una descripción del libro). Esto mostrará por qué los destructores son importantes.
#include <iostream>
#include <string>
using namespace std;
class Book {
private:
string title;
int pages;
string *description; // Memoria dinámica
public:
// Constructor
Book(string t, int p, string d)
: title(t), pages(p) {
description = new string(d);
cout << "Constructor de Book: " << title << endl;
}
// Destructor
~Book() {
cout << "Destructor de Book: " << title << endl;
delete description; // Liberar memoria dinámica
}
void Display() const {
cout << "Libro: " << title << " (" << pages << " páginas)" << endl;
cout << "Descripción: " << *description << endl;
}
};
int main() {
Book b1("1984", 328, "Una novela distópica.");
b1.Display();
}
Salida:
Constructor de Book: 1984
Libro: 1984 (328 páginas)
Descripción: Una novela distópica.
Destructor de Book: 1984
El destructor se llama automáticamente cuando el objeto en main() sale de su ámbito,
liberando la memoria asignada con new.
4. Sobrecarga de Constructores
Una clase puede tener múltiples constructores. El constructor apropiado se llama según el número y tipo de parámetros.
class Book {
private:
string title;
int pages;
public:
Book() : title("Desconocido"), pages(0) {}
Book(string t) : title(t), pages(100) {}
Book(string t, int p) : title(t), pages(p) {}
void Display() {
cout << "Libro: " << title << " (" << pages << " páginas)" << endl;
}
};
int main() {
Book b1;
Book b2("C++ 101");
Book b3("Patrones de Diseño", 450);
b1.Display();
b2.Display();
b3.Display();
}
5. Constructor de Copia
De forma predeterminada, C++ crea automáticamente un constructor de copia, pero si una clase utiliza memoria dinámica, se debe definir un constructor de copia personalizado.
class Book {
private:
string title;
string *desc;
public:
Book(string t, string d) {
title = t;
desc = new string(d);
cout << "Constructor: " << title << endl;
}
// Constructor de copia
Book(const Book &other) {
title = other.title;
desc = new string(*other.desc); // copia profunda
cout << "Constructor de copia: " << title << endl;
}
~Book() {
delete desc;
cout << "Destructor: " << title << endl;
}
void Display() {
cout << title << " - " << *desc << endl;
}
};
int main() {
Book b1("C++ Primer", "Guía para principiantes");
Book b2 = b1; // se llama al constructor de copia
b2.Display();
}
Si no se realiza una copia profunda (deep copy), ambos objetos compartirán la misma memoria, lo que provocará un error de “doble eliminación” cuando se ejecute el destructor.
6. Sistema de Libros – Seguimiento del Ciclo de Vida
El siguiente ejemplo muestra el orden en que se crea y destruye un objeto Book. Es ideal para entender el ciclo de vida real de los constructores y destructores.
#include <iostream>
#include <string>
using namespace std;
class Author {
public:
Author(string n) { cout << "Autor creado: " << n << endl; }
~Author() { cout << "Autor destruido." << endl; }
};
class Book {
private:
string title;
Author author;
public:
Book(string t, string a) : title(t), author(a) {
cout << "Libro creado: " << title << endl;
}
~Book() {
cout << "Libro destruido: " << title << endl;
}
};
int main() {
Book b("Rebelión en la granja", "George Orwell");
}
Salida:
Autor creado: George Orwell
Libro creado: Rebelión en la granja
Libro destruido: Rebelión en la granja
Autor destruido.
Como se puede ver, primero se llama al constructor de Author,
seguido por el constructor de Book.
Durante la destrucción ocurre el orden inverso (primero Book, luego Author).
Esto demuestra el orden del ciclo de vida en las relaciones de composición en C++.
7. TL;DR
- Constructor → Se llama automáticamente cuando se crea un objeto.
- Destructor → Se llama automáticamente cuando finaliza el ciclo de vida del objeto.
- Los constructores pueden sobrecargarse, los destructores no.
- Si se utiliza memoria dinámica, el destructor debe llamar a
delete. - El constructor de copia debe realizar una copia profunda.
- Orden de creación: primero los componentes, luego la clase; Orden de destrucción: primero la clase, luego los componentes.
- Todos los ejemplos pueden ejecutarse en Visual Studio 2022 o GCC 11+.