Gestión de memoria dinámica: new y delete
Asignaciones en heap, uso seguro de new/delete y preparación para RAII moderno.
En C++, la memoria se divide en dos regiones principales: Stack (pila) y Heap (montón).
Mientras que el área de pila se gestiona automáticamente, la memoria en el montón es controlada manualmente por el programador.
La gestión dinámica de memoria se utiliza para asignar y liberar memoria durante la ejecución del programa.
En este artículo, exploraremos en detalle el uso de las palabras clave new y delete para la gestión dinámica de memoria.
1. Diferencia entre Stack y Heap
| Característica | Stack (automático) | Heap (dinámico) |
|---|---|---|
| Gestión | Automática (por el compilador) | Manual (por el programador) |
| Duración | Se elimina automáticamente al final de la función | Permanece hasta que se libera manualmente |
| Velocidad | Muy rápida | Más lenta (gestión dinámica) |
| Tamaño de memoria | Limitado | Mucho más grande |
| Uso | Variables locales | Objetos y arreglos dinámicos |
2. Asignación de memoria con la palabra clave new
El operador new asigna dinámicamente memoria en el Heap y devuelve la dirección del espacio asignado.
Esta dirección normalmente se asigna a una variable puntero.
#include <iostream>
using namespace std;
int main() {
int *ptr = new int; // asigna memoria para un entero
*ptr = 42;
cout << "Valor: " << *ptr << endl;
delete ptr; // libera la memoria
ptr = nullptr; // hace el puntero seguro
}
Nota: La memoria asignada con new debe liberarse manualmente con delete.
De lo contrario, se producirá una fuga de memoria (memory leak).
3. Arreglos dinámicos con new
Para crear un arreglo dinámico, se utiliza new[].
Su tamaño puede determinarse durante la ejecución.
#include <iostream>
using namespace std;
int main() {
int n;
cout << "¿Cuántos elementos desea? ";
cin >> n;
int *arreglo = new int[n]; // crea un arreglo de n elementos
for (int i = 0; i < n; i++)
arreglo[i] = (i + 1) * 10;
cout << "Arreglo: ";
for (int i = 0; i < n; i++)
cout << arreglo[i] << " ";
delete[] arreglo; // libera la memoria
arreglo = nullptr;
}
El uso de delete[] es importante;
el delete normal solo se usa para una variable, no para arreglos.
4. Pasos para la asignación y liberación de memoria dinámica
- new → asigna memoria en el Heap.
- Puntero → almacena la dirección de la memoria asignada.
- Los datos se procesan o se llenan.
- delete / delete[] → libera la memoria asignada.
- Puntero = nullptr → previene punteros colgantes (dangling pointers).
5. ¿Qué es una fuga de memoria?
Si la memoria asignada dinámicamente no se libera, los bloques de memoria no utilizados se acumulan. Esto se llama una fuga de memoria y puede causar problemas de rendimiento en programas de larga duración.
void MalEjemplo() {
int *p = new int(10);
// delete p; // ¡Olvidado! Fuga de memoria
}
Solución: liberar siempre la memoria con delete.
6. nullptr y seguridad de memoria
Antes de C++11, los punteros nulos se representaban generalmente con NULL.
En el C++ moderno, se recomienda usar la palabra clave segura y tipada nullptr.
int *p = nullptr;
if (p == nullptr) {
cout << "El puntero no apunta a ninguna dirección." << endl;
}
Usar nullptr ayuda a prevenir accesos inválidos a memoria y errores de segmentación.
7. Uso de memoria dinámica con funciones
La memoria puede crearse dinámicamente y pasarse a una función. Esta técnica es especialmente útil con arreglos dinámicos.
void Llenar(int *p, int n) {
for (int i = 0; i < n; i++)
p[i] = (i + 1) * 2;
}
int main() {
int *arreglo = new int[5];
Llenar(arreglo, 5);
for (int i = 0; i < 5; i++)
cout << arreglo[i] << " ";
delete[] arreglo;
}
8. Creación de objetos con new
El operador new se puede usar no solo con tipos básicos,
sino también para crear objetos de clase.
#include <iostream>
using namespace std;
class Estudiante {
public:
string nombre;
Estudiante(string n) : nombre(n) {
cout << nombre << " creado." << endl;
}
~Estudiante() {
cout << nombre << " eliminado." << endl;
}
};
int main() {
Estudiante *p = new Estudiante("Ali");
delete p; // se llama al destructor
}
Eliminar un objeto creado con new llama automáticamente a su destructor.
9. Punteros inteligentes (enfoque moderno de C++)
Con C++11, se introdujeron los punteros inteligentes (smart pointers) como alternativa
a la liberación manual de memoria con delete.
Estos proporcionan una gestión automática de la memoria.
- std::unique_ptr → propiedad exclusiva (no copiable)
- std::shared_ptr → propiedad compartida (conteo de referencias)
- std::weak_ptr → referencia débil a un puntero compartido
#include <iostream>
#include <memory>
using namespace std;
int main() {
unique_ptr<int> p = make_unique<int>(100);
cout << *p << endl; // 100
}
Los punteros inteligentes liberan automáticamente la memoria cuando el objeto sale de su ámbito, eliminando el riesgo de fugas de memoria.
10. TL;DR
new→ asigna memoria en el Heap y devuelve su dirección.delete→ libera la memoria (variable única).new[] / delete[]→ se usan para arreglos dinámicos.nullptr→ definición segura para punteros nulos.- Para evitar fugas de memoria, cada
newdebe tener undeletecorrespondiente. std::unique_ptrystd::shared_ptrson alternativas modernas.- Todos los ejemplos pueden compilarse en Visual Studio 2022 o GCC 11+.