Vermeiden von Speicherlecks in C++

Jinku Hu 12 Oktober 2023
  1. Verwenden Sie den delete-Operator, um jeden new-allokierten Speicherblock freizugeben
  2. Verwenden Sie std::unique_ptr, um Heap-Objekte automatisch freizugeben
Vermeiden von Speicherlecks in C++

In diesem Artikel werden verschiedene Methoden zur Vermeidung von Speicherverlusten in C++ vorgestellt.

Verwenden Sie den delete-Operator, um jeden new-allokierten Speicherblock freizugeben

Speicherlecks sind ein häufiges Problem für Programme, die direkt mit der rohen dynamischen Speicherschnittstelle interagieren, was den Programmierer belastet, jeden zugewiesenen Speicher mit der entsprechenden Methode freizugeben. In C++ gibt es mehrere Schnittstellen für die Speicherverwaltung sowie einige plattformspezifische Systemaufrufe, die im Wesentlichen die Funktionen der untersten Ebene darstellen, die für diesen Zweck verwendet werden können. In den meisten Fällen sollte das reguläre Programm nur sprachspezifische Funktionen wie die Operatoren new und delete für die manuelle Speicherverwaltung verwenden oder intelligente Zeiger für die automatische Freigabe des Speichers verwenden.

Die anfälligste Methode für Speicherlecks ist die Verwendung von new/delete Operatoren zur direkten Verwaltung des Heapspeichers. Beachten Sie, dass jeder vom Aufruf new zurückgegebene Zeiger an den Operator delete übergeben werden sollte, um die entsprechenden Speicherblöcke wieder an das System freizugeben. Andernfalls wird der Speicher möglicherweise erschöpft. Das folgende Programm weist beispielsweise ein Array mit 10 Elementen char zu und gibt es vor dem Beenden des Programms nicht frei. Somit soll das Programm unter einem Speicherverlust leiden.

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

Speicherlecks können mit dem Programm valgrind erkannt werden, einem Befehlszeilenprogramm, das in einer kompilierten Programmdatei ausgeführt werden kann. Beachten Sie, dass Valgrind tatsächlich aus mehreren Tools besteht, von denen eines zufällig ein Dienstprogramm zur Speicherprüfung ist. Der folgende Befehl kann ausgegeben werden, um Speicherlecks zu untersuchen.

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

Nach der Überprüfung wird die folgende Ausgabe gedruckt. Beachten Sie, dass, da wir auf dem Zeiger arr nicht delete aufgerufen haben, das gesamte Array zu einem nicht freigegebenen Speicher führte, daher der Datensatz - definitely lost: 10 bytes in der Leckzusammenfassung. Letzteres ist die tatsächliche Größe des 10-Elemente-Char-Arrays.

==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)

Verwenden Sie std::unique_ptr, um Heap-Objekte automatisch freizugeben

Das vorherige Beispielcode-Problem kann durch delete [] arr; behoben werden. Anweisung, bevor das Programm beendet wird. Eine andere Möglichkeit besteht darin, einen der intelligenten Zeiger zu verwenden, z. std::unique_ptr. Das intelligente Zeigerobjekt kann mit dem angegebenen Typ initialisiert werden und speichert den vom Operator new zurückgegebenen Zeiger. Der einzige Unterschied zum regulären Zeiger besteht darin, dass der zugehörige Speicherbereich std::unique_ptr nicht explizit delete werden muss. Stattdessen kümmert sich der Compiler um die Freigabe, sobald das Objekt den Gültigkeitsbereich verlässt.

#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

Verwandter Artikel - C++ Memory