Destruktor — szczegółowe omówienie
Część sekcji Programowanie obiektowe ścieżki C++ w Coddy. Lekcja 24 z 104.
Destruktor to specjalna funkcja składowa, która uruchamia się automatycznie, gdy obiekt zostaje zniszczony. Jej głównym zadaniem jest zwalnianie zasobów, które obiekt pozyskał w trakcie swojego istnienia, takich jak dynamicznie alokowana pamięć, uchwyty do plików lub połączenia sieciowe.
Destruktor ma taką samą nazwę jak klasa, poprzedzoną tyldą ~. Nie przyjmuje żadnych parametrów i nie ma typu zwracanego:
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";
}
};Destruktory są wywoływane automatycznie w następujących sytuacjach:
- Obiekty na stosie wychodzą poza zakres
- Na obiekcie zaalokowanym na stercie wywoływane jest
delete - Kończy się czas życia obiektu tymczasowego
void example() {
FileHandler f1("data.txt", 100); // Wywołano konstruktor
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Wywołano destruktor dla f2
} // Wywołano destruktor dla f1 (wychodzi poza zakres)W przeciwieństwie do konstruktorów klasa może mieć tylko jeden destruktor. Nie można go przeciążać.
Jeśli go nie zdefiniujesz, kompilator wygeneruje domyślny destruktor, który nie robi niczego szczególnego — po prostu niszczy każdy element członkowski. W przypadku klas zarządzających zasobami musisz napisać własny, aby zapobiec wyciekom pamięci.
Wyzwanie
ŁatwyZbudujmy menedżer sesji, który śledzi aktywne sesje użytkowników i pokazuje, jak destruktory automatycznie zwalniają zasoby, gdy obiekty są niszczone — zarówno gdy wychodzą poza zakres, jak i gdy są jawnie usuwane.
Utworzysz dwa pliki, aby uporządkować swój kod:
Session.h: Zdefiniuj klasęSession, która reprezentuje aktywną sesję użytkownika z danymi zaalokowanymi dynamicznie. Twoja klasa powinna mieć:- Prywatne składowe:
username(string),sessionId(int) oraz wskaźnik do tablicy liczb całkowitych o nazwieactivityLog, która śledzi działania użytkownika, a takżelogSizeokreślający rozmiar tablicy - Konstruktor przyjmujący nazwę użytkownika, identyfikator sesji i rozmiar dziennika. Powinien zaalokować tablicę dziennika aktywności na stercie, zainicjować wszystkie elementy wartością 0 i wypisać
"Session <sessionId> started for <username>" - Destruktor, który zwalnia zaalokowaną pamięć i wypisuje
"Session <sessionId> ended for <username>" - Metodę
logActivity(int activityCode), która zapisuje kod aktywności w następnym dostępnym miejscu (bieżąca pozycja powinna być śledzona wewnętrznie); jeśli dziennik jest już pełny, wywołanie jest ignorowane, więc liczba nigdy nie przekracza jego rozmiaru - Metodę
getActivityCount(), która zwraca liczbę zapisanych aktywności - Metodę
getUsername(), która zwraca nazwę użytkownika
- Prywatne składowe:
main.cpp: Pokaż działanie destruktora w różnych sytuacjach. Odczytaj z wejścia nazwę użytkownika i rozmiar dziennika (każdą wartość w osobnym wierszu), a następnie:- Utwórz blok zakresu za pomocą nawiasów klamrowych
{ }, a wewnątrz niego utwórz obiektSessionalokowany na stosie, używając wczytanej nazwy użytkownika, identyfikatora sesji101i wczytanego rozmiaru dziennika. Zapisz dwie aktywności o kodach1i2, a następnie wypisz"Stack session activities: <count>". Po zakończeniu bloku destruktor powinien uruchomić się automatycznie. - Po bloku wypisz
"Stack session destroyed" - Utwórz obiekt
Sessionalokowany na stercie za pomocąnew, używając nazwy użytkownika"Guest", identyfikatora sesji202i rozmiaru dziennika3. Zapisz jedną aktywność o kodzie5, wypisz"Heap session activities: <count>", a następnie jawnie godelete. - Po usunięciu wypisz
"Heap session destroyed"
- Utwórz blok zakresu za pomocą nawiasów klamrowych
To wyzwanie pokazuje dwa główne sposoby wywoływania destruktorów: automatycznie, gdy obiekty alokowane na stosie wychodzą poza zakres, oraz ręcznie, gdy usuwasz obiekty alokowane na stercie. Obie ścieżki powinny poprawnie zwalniać dynamicznie zaalokowany dziennik aktywności, aby zapobiec wyciekom pamięci.
Dołącz plik nagłówkowy w pliku main.cpp za pomocą #include "Session.h".
Spróbuj swoich sił
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// Wczytaj dane wejściowe
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO: Utwórz blok zakresu za pomocą nawiasów klamrowych { }
// Wewnątrz bloku:
// - Utwórz Session na stosie, używając wczytanej nazwy użytkownika, identyfikatora sesji 101 i wczytanego rozmiaru dziennika
// - Zarejestruj dwie aktywności o kodach 1 i 2
// - Wypisz "Stack session activities: <count>"
// Po zakończeniu bloku destruktor uruchomi się automatycznie
// TODO: Po zakończeniu bloku wypisz "Stack session destroyed"
// TODO: Utwórz Session na stercie za pomocą new
// - Nazwa użytkownika: "Guest", identyfikator sesji: 202, rozmiar dziennika: 3
// - Zarejestruj jedną aktywność o kodzie 5
// - Wypisz "Heap session activities: <count>"
// - Usuń sesję
// TODO: Po usunięciu wypisz "Heap session destroyed"
return 0;
}
Ta lekcja zawiera krótki quiz. Zacznij lekcję, żeby na niego odpowiedzieć i śledzić swoje postępy.
Wszystkie lekcje w sekcji Programowanie obiektowe
1Podstawy programowania obiektowego
Pliki zewnętrzneBudowanie i kompilacja C++Pliki nagłówkowe i pliki źródłowePrzestrzenie nazw i zakresWprowadzenie do programowania obiektowego w C++Klasy a obiektyWskaźnik „this”Metody (funkcje składowe)Atrybuty (składowe danych)Podstawy konstruktorów i destruktorówPowtórzenie – prosty kalkulator4Właściwości klas
Elementy instancji a elementy statyczneGettery i setteryStałe funkcje składoweSłowo kluczowe mutableMetody i zmienne statyczneFunkcje i klasy zaprzyjaźnionePodsumowanie – menedżer konta bankowego7Dziedziczenie
Podstawy dziedziczeniaPoziomy dostępu w dziedziczeniuKolejność wywołań konstruktora i destruktoraNadpisywanie metodFunkcje wirtualne i VTableWielokrotne dziedziczenieDziedziczenie wirtualnePowtórzenie — hierarchia pracowników2Zarządzanie pamięcią
Pamięć stosu a stertyWskaźniki i referencjePamięć dynamiczna (new/delete)Inteligentne wskaźniki w C++RAII w C++Podsumowanie — menedżer tablicy dynamicznej5Hermetyzacja
Specyfikatory dostępu w C++Specyfikatory dostępu — szczegółowoUkrywanie informacjiStruktura a klasaKlasy zagnieżdżone i wewnętrznePodsumowanie — system ewidencji studentów8Polimorfizm
Polimorfizm czasu kompilacji i wykonaniaPrzeciążanie funkcjiFunkcje wirtualne — powtórkaCzysto wirtualne funkcjeKlasy abstrakcyjneProjektowanie interfejsów w C++Rzutowanie dynamiczne i RTTIPodsumowanie — kalkulator kształtów3Konstruktory i destruktory
Konstruktor domyślnyKonstruktor z parametramiKonstruktor kopiującyKonstruktor przenoszącyListy inicjalizacyjne konstruktoraKonstruktory delegująceDestruktor — szczegółowe omówienieZasada trzech / pięciu / zeraPodsumowanie — klasa String6Przeciążanie operatorów
Wprowadzenie do przeciążania operatorówPrzeciążanie operatorów arytmetycznychPrzeciążanie operatorów porównaniaOperatory strumieniowePrzeciążanie operatora przypisaniaPrzeciążanie operatorów [] i ()Operatory konwersji typówPodsumowanie — klasa Matrix9Szablony
Szablony funkcjiSzablony klasSpecjalizacja szablonówSzablony wariadycznePodstawy SFINAE i cech typówPodsumowanie — kontener generycznyPoćwicz samodzielnie: Kompilator C++ online