Evite pérdidas de memoria en C++

Jinku Hu 12 octubre 2023
  1. Utilice el operador delete para liberar cada bloque de memoria asignado new
  2. Utilice std::unique_ptr para liberar automáticamente los objetos del montón
Evite pérdidas de memoria en C++

Este artículo mostrará varios métodos sobre cómo evitar pérdidas de memoria en C++.

Utilice el operador delete para liberar cada bloque de memoria asignado new

Las pérdidas de memoria son un problema común para los programas que interactúan directamente con la interfaz de memoria dinámica sin procesar, lo que impone al programador una carga para liberar cada memoria asignada con el método correspondiente. Existen múltiples interfaces para la administración de memoria accesibles en C++ junto con algunas llamadas al sistema específicas de la plataforma, que esencialmente representan las funciones de nivel más bajo que se pueden utilizar para este propósito. En la mayoría de los casos, el programa normal solo debería utilizar facilidades específicas del idioma como operadores new y delete para la gestión manual de la memoria o utilizar punteros inteligentes para la desasignación automática de memoria.

El método más vulnerable para las fugas de memoria es utilizar el new/delete operadores para administrar directamente la memoria del montón. Tenga en cuenta que cada puntero devuelto por la llamada new debe pasarse al operador delete para liberar los bloques de memoria correspondientes al sistema, o la memoria podría agotarse. Por ejemplo, el siguiente programa asigna un array char de 10 elementos y no la libera antes de salir del programa. Por lo tanto, se dice que el programa sufre una pérdida de memoria.

#include <iostream>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  char *arr = new char[SIZE];

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}

Las pérdidas de memoria se pueden detectar utilizando el programa valgrind, que es una utilidad de línea de comandos y se puede ejecutar en un archivo de programa compilado. Tenga en cuenta que Valgrind es en realidad un conjunto de múltiples herramientas, una de las cuales es una utilidad de verificación de memoria. Se puede emitir el siguiente comando para investigar pérdidas de memoria.

valgrind --tool=memcheck --leak-check=full ./program

Una vez realizada la comprobación, se imprime la siguiente salida. Tenga en cuenta que, dado que no llamamos a delete en el puntero arr, toda el array resultó en memoria no liberada, de ahí el registro: definitivamente perdido: 10 bytes en el resumen de fugas. Este último es el tamaño real del array de caracteres de 10 elementos.

==13732== HEAP SUMMARY:
==13732==     in use at exit: 10 bytes in 1 blocks
==13732==   total heap usage: 3 allocs, 2 frees, 73,738 bytes allocated
==13732==
==13732== 10 bytes in 1 blocks are definitely lost in loss record 1 of 1
==13732==    at 0x483C583: operator new[](unsigned long) (in /usr/lib/x86_64-linux-gnu/valgrind/vgpreload_memcheck-amd64-linux.so)
==13732==    by 0x1091FE: main (tmp.cpp:11)
==13732==
==13732== LEAK SUMMARY:
==13732==    definitely lost: 10 bytes in 1 blocks
==13732==    indirectly lost: 0 bytes in 0 blocks
==13732==      possibly lost: 0 bytes in 0 blocks
==13732==    still reachable: 0 bytes in 0 blocks
==13732==         suppressed: 0 bytes in 0 blocks
==13732==
==13732== For lists of detected and suppressed errors, rerun with: -s
==13732== ERROR SUMMARY: 1 errors from 1 contexts (suppressed: 0 from 0)

Utilice std::unique_ptr para liberar automáticamente los objetos del montón

El problema del código de ejemplo anterior se puede solucionar con delete [] arr; declaración antes de que salga el programa. Aunque, otra opción es utilizar uno de los punteros inteligentes, p. Ej. std::unique_ptr. El objeto de puntero inteligente se puede inicializar con el tipo dado y almacena el puntero devuelto por el operador new. La única diferencia con el puntero normal es que la región de memoria asociada std::unique_ptr no necesita ser delete explícitamente. En cambio, el compilador se encarga de la desasignación una vez que el objeto sale del alcance.

#include <iostream>
#include <memory>

using std::cout;
using std::endl;

constexpr int SIZE = 10;

int main() {
  std::unique_ptr<char[]> arr(new char[SIZE]);

  for (int i = 0; i < SIZE; ++i) {
    arr[i] = (char)(65 + i);
    cout << arr[i] << "; ";
  }
  cout << endl;

  return EXIT_SUCCESS;
}
Autor: Jinku Hu
Jinku Hu avatar Jinku Hu avatar

Founder of DelftStack.com. Jinku has worked in the robotics and automotive industries for over 8 years. He sharpened his coding skills when he needed to do the automatic testing, data collection from remote servers and report creation from the endurance test. He is from an electrical/electronics engineering background but has expanded his interest to embedded electronics, embedded programming and front-/back-end programming.

LinkedIn Facebook

Artículo relacionado - C++ Memory