Evite vazamentos de memória em C++

Jinku Hu 12 outubro 2023
  1. Use o operador delete para liberar cada new bloco de memória alocado
  2. Use std::unique_ptr para liberar objetos Heap automaticamente
Evite vazamentos de memória em C++

Este artigo demonstrará vários métodos sobre como evitar vazamentos de memória em C++.

Use o operador delete para liberar cada new bloco de memória alocado

Vazamentos de memória são um problema comum para os programas que interagem diretamente com a interface de memória dinâmica bruta, o que sobrecarrega o programador para liberar cada memória alocada com o método correspondente. Existem várias interfaces para gerenciamento de memória acessíveis em C++, juntamente com algumas chamadas de sistema específicas da plataforma, que representam essencialmente as funções de nível mais baixo que podem ser utilizadas para essa finalidade. Na maioria dos casos, o programa regular deve usar apenas recursos específicos da linguagem, como operadores new e delete para gerenciamento manual de memória ou usar ponteiros inteligentes para desalocação automática de memória.

O método mais vulnerável para vazamentos de memória é usar o new/delete operadores para gerenciar diretamente a memória heap. Observe que cada ponteiro retornado da chamada new deve ser passado para o operador delete para liberar os blocos de memória correspondentes de volta para o sistema, ou a memória pode se esgotar. Por exemplo, o programa a seguir aloca um array char de 10 elementos e não a libera antes da saída do programa. Portanto, diz-se que o programa sofre de um vazamento de memória.

#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;
}

Vazamentos de memória podem ser detectados usando o programa valgrind, que é um utilitário de linha de comando e pode ser executado em um arquivo de programa compilado. Observe que Valgrind é na verdade um conjunto de várias ferramentas, uma das quais é um utilitário de verificação de memória. O seguinte comando pode ser emitido para investigar vazamentos de memória.

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

Depois de concluída a verificação, a seguinte saída é impressa. Observe que, uma vez que não chamamos delete no ponteiro arr, todo o array resultou em memória não alimentada, portanto, o registro - definitivamente perdido: 10 bytes no resumo de vazamento. O último é o tamanho real do array char 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)

Use std::unique_ptr para liberar objetos Heap automaticamente

O problema de código do exemplo anterior pode ser corrigido pelo método delete [] arr; declaração antes de o programa terminar. Embora, outra opção seja utilizar um dos ponteiros inteligentes, por exemplo, std::unique_ptr. O objeto de ponteiro inteligente pode ser inicializado com o tipo fornecido e armazena o ponteiro retornado do operador new. A única diferença do ponteiro regular é que a região de memória associada std::unique_ptr não precisa ser delete explicitamente. Em vez disso, o compilador cuida da desalocação quando o objeto sai do escopo.

#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

Artigo relacionado - C++ Memory