Destruktoren im Detail
Teil des Abschnitts Objektorientierte Programmierung der C++-Journey von Coddy — Lektion 24 von 104.
Ein Destruktor ist eine spezielle Elementfunktion, die automatisch ausgeführt wird, wenn ein Objekt zerstört wird. Seine Hauptaufgabe besteht darin, Ressourcen freizugeben, die das Objekt während seiner Lebensdauer erworben hat, wie z. B. dynamisch zugewiesener Speicher, Dateihandles oder Netzwerkverbindungen.
Der Destruktor hat denselben Namen wie die Klasse, vorangestellt mit einer Tilde ~. Er nimmt keine Parameter entgegen und hat keinen Rückgabetyp:
class FileHandler {
std::string filename;
int* buffer;
public:
FileHandler(std::string name, size_t size)
: filename(name), buffer(new int[size]) {
std::cout << "Opening " << filename << "\n";
}
~FileHandler() {
delete[] buffer;
std::cout << "Closing " << filename << "\n";
}
};Destruktoren werden in diesen Situationen automatisch aufgerufen:
- Stack-Objekte verlassen den Gültigkeitsbereich
deletewird für ein auf dem Heap alloziertes Objekt aufgerufen- Die Lebensdauer eines temporären Objekts endet
void example() {
FileHandler f1("data.txt", 100); // Konstruktor aufgerufen
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Destruktor für f2 aufgerufen
} // Destruktor für f1 aufgerufen (verlässt den Gültigkeitsbereich)Im Gegensatz zu Konstruktoren kann eine Klasse nur einen Destruktor haben. Sie können ihn nicht überladen.
Wenn Sie keinen definieren, generiert der Compiler einen Standard-Destruktor, der nichts Besonderes tut – er zerstört einfach jedes Element. Für Klassen, die Ressourcen verwalten, müssen Sie einen eigenen schreiben, um Speicherlecks zu verhindern.
Aufgabe
EinfachLassen Sie uns einen Sitzungsmanager erstellen, der aktive Benutzersitzungen verfolgt und demonstriert, wie Destruktoren Ressourcen automatisch bereinigen, wenn Objekte zerstört werden — unabhängig davon, ob sie den Gültigkeitsbereich verlassen oder explizit gelöscht werden.
Sie werden zwei Dateien erstellen, um Ihren Code zu organisieren:
Session.h: Definieren Sie eineSession-Klasse, die eine aktive Benutzersitzung mit dynamisch zugewiesenen Daten darstellt. Ihre Klasse sollte Folgendes enthalten:- Private Member: einen
username(string), einesessionId(int) und einen Zeiger auf ein Integer-Array namensactivityLog, das Benutzeraktionen verfolgt, zusammen mit einerlogSizefür die Array-Größe - Einen Konstruktor, der einen Benutzernamen, eine Sitzungs-ID und eine Protokollgröße entgegennimmt. Er sollte das activity log-Array auf dem Heap reservieren, alle Elemente mit 0 initialisieren und
"Session <sessionId> started for <username>"ausgeben - Einen Destruktor, der den reservierten Speicher freigibt und
"Session <sessionId> ended for <username>"ausgibt - Eine
logActivity(int activityCode)-Methode, die den Aktivitätscode im nächsten verfügbaren Slot speichert (verfolgen Sie die aktuelle Position intern) - Eine
getActivityCount()-Methode, die zurückgibt, wie viele Aktivitäten protokolliert wurden - Eine
getUsername()-Methode, die den Benutzernamen zurückgibt
- Private Member: einen
main.cpp: Demonstrieren Sie das Verhalten des Destruktors in verschiedenen Szenarien. Lesen Sie einen Benutzernamen und eine Protokollgröße von der Eingabe ein (jeweils in einer separaten Zeile), dann:- Erstellen Sie einen Scope-Block mit geschweiften Klammern
{ }und erstellen Sie darin eine Stack-allozierteSessionmit dem eingegebenen Benutzernamen, der Sitzungs-ID101und der eingegebenen Protokollgröße. Protokollieren Sie zwei Aktivitäten mit den Codes1und2, und geben Sie dann"Stack session activities: <count>"aus. Wenn der Block endet, sollte der Destruktor automatisch ausgeführt werden. - Geben Sie nach dem Block
"Stack session destroyed"aus - Erstellen Sie eine Heap-allozierte
Sessionmitnew, dem Benutzernamen"Guest", der Sitzungs-ID202und der Protokollgröße3. Protokollieren Sie eine Aktivität mit dem Code5, geben Sie"Heap session activities: <count>"aus und löschen Sie sie dann explizit mitdelete. - Geben Sie nach dem Löschen
"Heap session destroyed"aus
- Erstellen Sie einen Scope-Block mit geschweiften Klammern
Diese Herausforderung zeigt die zwei Hauptwege, auf denen Destruktoren aufgerufen werden: automatisch, wenn Stack-Objekte ihren Gültigkeitsbereich verlassen, und manuell, wenn Sie Heap-Objekte löschen. Beide Pfade sollten das dynamisch allozierte Aktivitätsprotokoll ordnungsgemäß freigeben, um Speicherlecks zu vermeiden.
Binden Sie Ihre Header-Datei in main.cpp mit #include "Session.h" ein.
Spickzettel
Ein Destruktor ist eine spezielle Elementfunktion, die automatisch ausgeführt wird, wenn ein Objekt zerstört wird. Er gibt Ressourcen wie dynamisch zugewiesenen Speicher, Dateihandles oder Netzwerkverbindungen frei.
Der Destruktor hat denselben Namen wie die Klasse, ergänzt um eine vorangestellte Tilde ~. Er nimmt keine Parameter entgegen und hat keinen Rückgabetyp:
class FileHandler {
std::string filename;
int* buffer;
public:
FileHandler(std::string name, size_t size)
: filename(name), buffer(new int[size]) {
std::cout << "Opening " << filename << "\n";
}
~FileHandler() {
delete[] buffer;
std::cout << "Closing " << filename << "\n";
}
};Destruktoren werden automatisch aufgerufen, wenn:
- Stack-Objekte den Gültigkeitsbereich verlassen
deletefür ein auf dem Heap alloziertes Objekt aufgerufen wird- Die Lebensdauer eines temporären Objekts endet
void example() {
FileHandler f1("data.txt", 100); // Konstruktor aufgerufen
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Destruktor für f2 aufgerufen
} // Destruktor für f1 aufgerufen (verlässt den Gültigkeitsbereich)Eine Klasse kann nur einen Destruktor haben (kein Überladen). Wenn Sie keinen definieren, generiert der Compiler einen Standard-Destruktor. Für Klassen, die Ressourcen verwalten, müssen Sie einen eigenen schreiben, um Speicherlecks zu vermeiden.
Probier es selbst
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// Eingabe lesen
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO: Erstelle einen Scope-Block mit geschweiften Klammern { }
// Innerhalb des Blocks:
// - Erstelle eine Stack-allozierte Session mit dem eingegebenen username, der session ID 101 und der eingegebenen log size
// - Protokolliere zwei Aktivitäten mit den Codes 1 und 2
// - Gib "Stack session activities: <count>" aus
// Wenn der Block endet, wird der Destruktor automatisch ausgeführt
// TODO: Gib nach dem Block "Stack session destroyed" aus
// TODO: Erstelle eine Heap-allozierte Session mit new
// - Username: "Guest", session ID: 202, log size: 3
// - Protokolliere eine Aktivität mit dem Code 5
// - Gib "Heap session activities: <count>" aus
// - Lösche die Session mit delete
// TODO: Gib nach dem Löschen "Heap session destroyed" aus
return 0;
}
Diese Lektion enthält ein kurzes Quiz. Starte die Lektion, um es zu beantworten und deinen Fortschritt zu speichern.
Alle Lektionen in Objektorientierte Programmierung
1Grundlagen der OOP
Externe DateienC++ Build & KompilierungHeader-Dateien & QuelldateienNamensräume & GültigkeitsbereicheEinführung in OOP in C++Klassen vs. ObjekteDer 'this'-PointerMethoden (Elementfunktionen)Attribute (Datenelemente)Grundlagen zu Ctors & DtorsRückblick - Einfacher Taschenrechner4Klasseneigenschaften
Instanz- vs. statische MemberGetter und SetterConst-MemberfunktionenMutable-SchlüsselwortStatische Methoden und VariablenFriend-Funktionen & KlassenZusammenfassung - Bankkonto-Manager7Vererbung
Grundlagen der VererbungZugriffsstufen bei VererbungAufrufreihenfolge von Ctor & DtorÜberschreiben von MethodenVirtuelle Funktionen & VTableMehrfachvererbungVirtuelle VererbungRückblick - Mitarbeiter-Hierarchie2Speicherverwaltung
Stack vs. Heap SpeicherZeiger und ReferenzenDynamischer Speicher (new/delete)Smart Pointers in C++RAII in C++Rückblick – Dynamischer Array-Manager5Kapselung
Zugriffsspezifizierer in C++Zugriffsspezifizierer im DetailInformation HidingStruct vs. ClassVerschachtelte & innere KlassenRückblick - Studentenverwaltungssystem8Polymorphie
Kompilierzeit- vs. Laufzeit-PolymorphieFunktionsüberladungVirtuelle Funktionen – WiederholungRein virtuelle FunktionenAbstrakte KlassenInterface-Design in C++Dynamic Casting & RTTIZusammenfassung – Formen-Rechner3Konstruktoren & Destruktoren
StandardkonstruktorParametrisierter KonstruktorKopierkonstruktorVerschiebekonstruktorKonstruktor-InitialisierungslistenDelegierende KonstruktorenDestruktoren im DetailRule of Three / Five / ZeroZusammenfassung - String-Klasse6Operatorüberladung
Einführung in die OperatorüberladungArithmetische OperatorüberladungVergleichsoperatorüberladungStream-OperatorenZuweisungsoperatorüberladungÜberladen der [] und () OperatorenTypumwandlungsoperatorenRückblick - Matrix-Klasse9Templates
FunktionstemplatesKlassentemplatesTemplate-SpezialisierungVariadische TemplatesSFINAE & Type Traits GrundlagenRückblick - Generische Container