Memoria dinámica (new/delete)
Parte de la sección Programación Orientada a Objetos del Journey de C++ de Coddy — lección 14 de 104.
Ahora que entiendes los punteros y las referencias, pongámoslos a trabajar con la asignación dinámica de memoria. Los operadores new y delete te permiten crear y destruir objetos en el heap en tiempo de ejecución.
Usa new para asignar memoria y obtener un puntero a ella:
int* num = new int; // Asignar un solo entero
*num = 42; // Asignar valor a través del puntero
Player* player = new Player("Hero"); // Asignar objetoCada new debe tener un delete correspondiente para liberar la memoria:
delete num; // Liberar un solo objeto
delete player; // Llama al destructor, luego libera la memoriaPara arrays, usa new[] y delete[]:
int* scores = new int[5]; // Asignar un array de 5 enteros
scores[0] = 100;
delete[] scores; // Liberar el array - ¡nota los corchetes!Errores comunes a evitar:
// Fuga de memoria - se olvidó de liberar (delete)
int* ptr = new int(10);
ptr = new int(20); // ¡Se perdió el acceso a la primera asignación!
// Puntero colgante - uso después de borrar (delete)
delete ptr;
*ptr = 5; // ¡Comportamiento indefinido!
// Uso incorrecto de delete
int* arr = new int[10];
delete arr; // ¡Incorrecto! Debería ser delete[]Después de eliminar, establece los punteros a nullptr para evitar su reutilización accidental. La gestión manual de la memoria es propensa a errores, razón por la cual el C++ moderno prefiere los punteros inteligentes, que exploraremos a continuación.
Desafío
FácilVamos a construir un sistema simple de almacenamiento de mensajes que demuestre la asignación dinámica de memoria con new y delete.
Crearás dos archivos para organizar tu código:
MessageBox.h: Define una claseMessageBoxque gestione dinámicamente un arreglo de mensajes. La clase debe tener:- Un puntero privado a un arreglo de cadenas (strings) asignado dinámicamente
- Un entero privado que rastree el conteo actual de mensajes
- Un entero privado que almacene la capacidad máxima
- Un constructor que reciba una capacidad y asigne el arreglo usando
new[] - Un destructor que libere adecuadamente la memoria usando
delete[]e imprima"MessageBox destroyed" - Un método
addMessage(string msg)que añada un mensaje si hay espacio, devolviendotrueen caso de éxito ofalsesi está lleno - Un método
printAll()que imprima cada mensaje almacenado en su propia línea
main.cpp: Lee un valor de capacidad y un número de mensajes desde la entrada. Luego lee esa cantidad de cadenas de mensajes. Crea unMessageBoxen el heap usandonew, añádele todos los mensajes, llama aprintAll()y luego elimina adecuadamente el MessageBox para liberar memoria.
El formato de entrada será:
- Primera línea: la capacidad (entero)
- Segunda línea: el número de mensajes a añadir (entero)
- Líneas siguientes: un mensaje por línea
Recuerda: cada new necesita un delete correspondiente, y cada new[] necesita un delete[] correspondiente. Después de eliminar, es una buena práctica establecer los punteros a nullptr.
Incluye tu archivo de cabecera en main.cpp usando #include "MessageBox.h".
Hoja de referencia
Usa new para asignar memoria dinámicamente en el heap y obtener un puntero a ella:
int* num = new int; // Allocate single integer
*num = 42; // Assign value through pointer
Player* player = new Player("Hero"); // Allocate objectCada new debe tener un delete correspondiente para liberar la memoria:
delete num; // Free single object
delete player; // Calls destructor, then frees memoryPara arreglos, usa new[] y delete[]:
int* scores = new int[5]; // Allocate array of 5 integers
scores[0] = 100;
delete[] scores; // Free array - note the brackets!Errores comunes a evitar:
// Memory leak - forgot to delete
int* ptr = new int(10);
ptr = new int(20); // Lost access to first allocation!
// Dangling pointer - using after delete
delete ptr;
*ptr = 5; // Undefined behavior!
// Mismatched delete
int* arr = new int[10];
delete arr; // Wrong! Should be delete[]Después de borrar, establece los punteros a nullptr para evitar su reutilización accidental.
Pruébalo tú mismo
#include <iostream>
#include <string>
#include "MessageBox.h"
using namespace std;
int main() {
int capacity;
int numMessages;
// Leer la capacidad y el número de mensajes
cin >> capacity;
cin >> numMessages;
cin.ignore(); // Limpiar el salto de línea después del número
// TODO: Crear un MessageBox en el heap usando new
// TODO: Leer y añadir todos los mensajes al MessageBox
for (int i = 0; i < numMessages; i++) {
string message;
getline(cin, message);
// Añadir el mensaje al MessageBox
}
// TODO: Llamar a printAll() para mostrar todos los mensajes
// TODO: Eliminar el MessageBox para liberar memoria
// Recuerda establecer el puntero a nullptr después de eliminar
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