Enviado por Kalith en C/C++, Programacion
Sobre punteros, memoria dinámica y otras hierbas alucinogenas.
“La gestión manual de bloques de memoria en C es como hacer malabarismos con pastillas de jabón en la ducha de la prisión: todo diversión hasta que cometes un fallo”
Empezando con esta conocida frase de humor vamos a empezar a tocar un tema bastante rudo, sobre todo para los nuevos en este mundo, confieso que tengo una experiencia en mi trabajo como acesor me toco explicar entre otras cosas memoria dinámica y no me fue del todo bien. Ok supondre que nuestro lector, ya sabe el concepto de puntero y no me detendre a tratar esos detalles.
Empezemos que es la memoria dinámica? Bueno es un termino que se le da a la memoria que no es creada estaticamente (me recordo esa deducción a la profesora de Llyn a = a) y es puesta sobre el heap, digamos algo asi, estaba cierta vez un puntero triste y solo en su celda de memoria y un conoce a una hermosa puntero y deciden casarse y tener hijos pero no saben donde guardarlos entonces le dicen a su amigo el heap, padre de el tiempo y el espacio computacional junto al stack que le de un sitio donde guardar a sus hijos entonces el heap le da un trozito de vivienda con una unica condición que la devuelva cuando la deje de usar, ahora diran ustedes: “Vaya analogía de miarda” y no, no estoy drogado pero digamos que sería una somera idea de como trabaja la alocación dinámica.
Supongamos que necesitamos almacenar en un buffer de enteros N elementos pero ese N no lo sabremos si no en tiempo de ejecución, como hacemos? bueno un hereje podría decir, hagamos un buffer lo suficientemente grande que este seguro que el tamaño no se va a pasar de ahi, pero un masoca como yo diría aloquemos dinámicamente.
int * buffer = new int[N]
donde el N fue un dato introducido, veamos pero la historia de los punteros promiscuos el dios heap le dijo a los punteros que regresaran la casa cuando la dejaran de usar, entonces como devolvemos esa memoria alocada?
delete [] buffer
funciona igual con un tipo de dato definido por el usuario? pues claro que si! veamos la clase A
class A {
public:
A();
A(int a);
~A();
void soy_un_feliz_metodo();
private:
int _a;
};
A::A() { cout << "Soy un constructor" << endl; }
A::A(int a) {
this->_a = a;
cout << "Soy un constructor con parametro" << endl;
}
A::~A() { cout << "Soy un destructor" << endl; }
void A::soy_un_feliz_metodo() { cout << "Soy un metodo" << endl; }
ahora bien crearemos un objeto dinámicamente de esta clase.
A* punterofeliz = new A(200);
cuando el programe cargue veran el mensaje “Soy un constructor con parametro” pero que pasa cuando lo liberamos?
delete punterofeliz;
exacto vieron el mensaje “Soy un destructor”, de ahi es donde nace la necesidad de usar delete y no free como en C puesto que imaginemos que dentro de nuestra clase alocaramos memoria y la liberamos con la llamada al destructor, delete se encarga de llamar al destructor de la clase antes de ser desalocada la memoria de si mismo. Asi se evita la fuga de memoria, pero que pasa si no es un objeto el que debemos alocar sino que son muchos. Pues veamos:
A* punteropromiscuo = new A[N];
Ok aca pues si se fijaron no use el constructor sobrecargado que recibía un parametro como entero, esto en c++ no se puede si lo intentan les dara un error grande como una casa, y si se dieron cuenta habrá N mensajes “Soy un constructor” puesto que se hacen N llamadas al constructor por defecto. Y como lo liberamos, les doy un tip porque hay muchas personas por absurdo que parezca si usan [] para construir con ese liberan si no lo usan pues al liberar no se debe llamar
delete [] punteropromiscuo;
Hasta ahora todo va bien, que pasa si necesitamos crear una mtriz de N*K dimensiones pero obviamente de forma dinámica, pues lo que haremos sera un puntero de N posiciones que apunte a punteros de K dimensiones, hay varias formas de hacerlo, yo lo haré por la que uso yo, por ser mas comodo.
int** ptr; /* ... */ ptr = new int*[N]; for (size_t i = 0; i < N; ++i) ptr[i] = new int[K];
que fue lo que hicimos? pues aloque el puntero de tamaño N y luego me posicione en cada una de esas N posiciones y aloque uno de tamaño K, facil verdad? ahora como desalocamos esta cosa? La misma idea nos paramos en cada una de las N posiciones vamos desalocando y luego desalocamos el contenedor principal
for (size_t i = 0; i < N; ++i) delete [] ptr[i]; delete [] ptr;
;
imaginemos algo, recuerdan la historia de los punteros? imaginen que llegó una rara época y las hormonas de los punteros estan a radiar y se empiezan a reproducir entre ellos y ya no quedan casas de vivir entonces que hace el heap? pues evidentemente se queda sin memoria, que podemos hacer nosotros? veamos, tenemos esta clase:
class ClaseGorda {
private:
long long a;
long long b;
long long c;
long abc[10000];
};
y vamos a alocar un gran tamaño:
ClaseGorda* estoybiengordo = new ClaseGorda[100000];
Visual Studio directamente no compila esa cosa, pero otros compiladores de menos calidad (Vease Gcc) si te dejan compilar pero que hace? el programa explota. Que haremos? pues llamar a un manejador para este tipo de eventos cuando la memoria se acabe.
#include <iostream>
#include <cstdlib>
#include <new>
using namespace std;
class ClaseGorda {
private:
long long a;
long long b;
long long c;
long abc[10000];
};
void mensaje_de_error() {
cout << "Hey!!! Te comiste toda la memoria" << endl;
exit(1);
}
int main(void){
set_new_handler(mensaje_de_error);
ClaseGorda* ptr = new ClaseGorda[100000];
system("pause");
}
que pasara aca? pues que nos va a dar un mensaje de error “Hey!!! Te comiste toda la memoria” y saldra del programa. Bueno ha sido todo por hoy ya me canse de escribir, en otra oportunidad seguire con este tema.
Que la fuerza los acompañe.
Posts Relacionados
- Transformar numero de base n a base k Buenas. Realmente lo que trata este post es poder transformar...
- Suma de dos números en base ‘n’ Tenemos dos números como cadena de caracteres, ambas en la...


