Bienvenidos sean a este post, hablemos sobre como manejar la memoria.
En los posts anteriores, hablamos sobre los distintos tipos de memoria. Tambien mencionamos que una posibilidad que puede suceder son las fugas de memoria (memory leaks) por no liberar un espacio de memoria asignado. Una cosa que no comentamos, cuando asignamos un espacio de memoria el S.O lo marca como busy (ocupado) y a partir de este instante mientras el programa este corriendo, nadie podra usar ese espacio de memoria. Por esta razon, el programa debe encargarse de liberar el espacio en memoria para que el S.O sepa que puede reutilizar ese espacio liberado. Si bien, algunos lenguajes lo manejan automaticamente por nosotros. En el caso de C++ no es asi, por lo tanto la administracion dinamica queda bajo responsabilidad del programador. Por esta razon, existen las funciones new y delete para manejar el espacio de memoria. Veamos un ejemplo de como seria:
T* p = new T();
p->hace_algo();
delete p;
Primero ubicamos un espacio en memoria, luego trabajamos con este espacio, para finalmente liberarlo. Vamos a suponer que no ejecutamos esta ultima linea. Este espacio quedara con el estado busy para siempre. Bueno, para siempre no. Porque una vez que finalice el programa se liberara ese espacio en memoria pero mientras este corriendo no estara disponible. Este inconviente puede suceder con programas que utilizan socket de conexion de red o un archivo, donde si olvidamos cerrarlo quedaran abiertos sin hacer nada. Para resolver esto en C++, se utiliza una tecnica llamada RAII, Adquisicion de Recursos Es Inicializacion, para establecer que el objeto debe ser adquirido en su inicializacion, lo cual nos permitira liberarlo mas facilmente. Vamos a analizar un ejemplo simple:
#include <iostream>
#include <algorithm>
void mostrar_ord()
{
short *arr{new short[5]};
for (int i=0; i < 5; i++)
{
std::cout << "Valor " << i << ": ";
std::cin >> arr[i];
}
std::sort(arr, arr + 5);
for(int i = 0; i < 5; i++)
std::cout << arr[i] << std::endl;
delete arr;
}
int main()
{
mostrar_ord();
return 0;
}
Primero definiremos una funcion que usaremos para almacenar unos valores dentro de un array y luego mostrarlos ordenados. Para ello usaremos a sort, por esta razon necesitaremos a la libreria algorithm. Para ello, primero declaramos un array con diez posiciones. Luego, mediante un bucle agregaremos un valor a cada posicion del array anterior. Una vez finalizado, usaremos al sort para ordenarlo y para ello pasaremos la primera posicion y la ultima. Con esto realizado, usamos un bucle para mostrar cada una de las posiciones ahora ordenadas. Para finalmente, borrar el array. En el main, llamamos a la funcion anterior. Con esto comentado, compilemos y ejecutemos para ver como trabaja:
$ ./memoria
Valor 0: 10
Valor 1: 2
Valor 2: 5
Valor 3: 1
Valor 4: 6
1
2
5
6
10
$
Como pueden ver funciono perfectamente pero ese codigo tiene un error. Al momento de eliminar el array usamos a delete y esto eliminara solo la primera posicion dejando al resto sueltas en memoria. Para solucionar esto, solamente debemos cambiarlo de la siguiente manera:
delete[] arr;
Esto le indica al programa que debe eliminar a todo el conjunto de posiciones del array. Recuerden que el array es un conjunto de posiciones secuenciales a partir de la primera que se crea. Si solo eliminaramos el primer puntero, los siguientes quedaran huerfanos y no podremos eliminarlos porque el que sirve de referencia ya no existe. Tomemos el codigo anterior y modifiquemos la funcion mostrar_ord de la siguiente manera:
void mostrar_ord()
{
short *arr{new short[5]};
for (int i=0; i < 5; i++)
{
std::cout << "Valor " << i << ": ";
std::cin >> arr[i];
if (arr[i] < 0) return;
}
std::sort(arr, arr + 5);
for(int i = 0; i < 5; i++)
std::cout << arr[i] << std::endl;
delete[] arr;
}
Aqui agregamos un condicional para que ahora la funcion cuando reciba un valor negativo procede a salir de la misma. Compilemos y veamos como trabaja ahora:
$ ./memoria
Valor 0: 1
Valor 1: 2
Valor 2: -1
$
Cuando salimos de la funcion dejo el array en memoria porque omitio todo lo que seguia. Por ende, volvemos a tener el mismo inconveniente que antes dejando elementos huerfanos en memoria. Tomemos el condicional nuevo y hagamos la siguiente modificacion:
if (arr[i] < 0) {
delete[] arr;
return;
}
Esta simple modificacion nos permite eliminar el array que se haya creado, y evitando que esto suceda. Estos son casos simples para entender el concepto de por que debemos tener cuidado con las fugas de memoria. Estos fueron casos sencillos y pequeños pero imaginen que esto sucede con arrays de muchisimos mas elementos y por ende, ocupando mucho mas espacio en memoria. Esto nos da pie a unos temas que veremos en los proximos posts.
En resumen, hoy hemos visto como administrar la memoria, algunos conceptos basicos, algunos temas que pueden surgir, asi como otros problemas que pueden surgir y como solucionarlos. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donatión
It’s for site maintenance, thanks!
$1.50
