Vermeiden von Speicherlecks in C++
-
Verwenden Sie den
delete
-Operator, um jedennew
-allokierten Speicherblock freizugeben -
Verwenden Sie
std::unique_ptr
, um Heap-Objekte automatisch freizugeben
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;
}
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