Cargando...

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ísticaStack (automático)Heap (dinámico)
GestiónAutomática (por el compilador)Manual (por el programador)
DuraciónSe elimina automáticamente al final de la funciónPermanece hasta que se libera manualmente
VelocidadMuy rápidaMás lenta (gestión dinámica)
Tamaño de memoriaLimitadoMucho más grande
UsoVariables localesObjetos 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

  1. new → asigna memoria en el Heap.
  2. Puntero → almacena la dirección de la memoria asignada.
  3. Los datos se procesan o se llenan.
  4. delete / delete[] → libera la memoria asignada.
  5. 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.


#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 new debe tener un delete correspondiente.
  • std::unique_ptr y std::shared_ptr son alternativas modernas.
  • Todos los ejemplos pueden compilarse en Visual Studio 2022 o GCC 11+.

Artículos relacionados