Approfondimento sui distruttori
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 24 di 104.
Un distruttore è una funzione membro speciale che viene eseguita automaticamente quando un oggetto viene distrutto. Il suo compito principale è rilasciare le risorse acquisite dall’oggetto durante la sua vita, come la memoria allocata dinamicamente, gli handle di file o le connessioni di rete.
Il distruttore ha lo stesso nome della classe, preceduto da una tilde ~. Non accetta parametri e non ha un tipo di ritorno:
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";
}
};I distruttori vengono chiamati automaticamente nelle seguenti situazioni:
- Gli oggetti dello stack escono dall'ambito
- Viene chiamato
deletesu un oggetto allocato nell'heap - La durata di vita di un oggetto temporaneo termina
void example() {
FileHandler f1("data.txt", 100); // Costruttore chiamato
FileHandler* f2 = new FileHandler("log.txt", 50);
delete f2; // Distruttore chiamato per f2
} // Distruttore chiamato per f1 (esce dall'ambito)A differenza dei costruttori, una classe può avere un solo distruttore. Non puoi sovraccaricarlo.
Se non ne definisci uno, il compilatore genera un distruttore predefinito che non fa nulla di speciale: si limita a distruggere ogni membro. Per le classi che gestiscono risorse, devi scriverne uno tuo per evitare perdite di memoria.
Sfida
FacileCreiamo un gestore di sessioni che tiene traccia delle sessioni utente attive e dimostra come i distruttori liberano automaticamente le risorse quando gli oggetti vengono distrutti, sia quando escono dall'ambito sia quando vengono eliminati esplicitamente.
Creerai due file per organizzare il codice:
Session.h: definisci una classeSessionche rappresenta una sessione utente attiva con dati allocati dinamicamente. La classe deve avere:- membri privati: uno
username(stringa), unsessionId(int) e un puntatore a un array di interi chiamatoactivityLogche tiene traccia delle azioni dell'utente, insieme a unlogSizeper la dimensione dell'array - Un costruttore che accetta un nome utente, un ID sessione e la dimensione del log. Deve allocare l'array del log delle attività nell'heap, inizializzare tutti gli elementi a 0 e stampare
"Session <sessionId> started for <username>" - Un distruttore che libera la memoria allocata e stampa
"Session <sessionId> ended for <username>" - Un metodo
logActivity(int activityCode)che memorizza il codice dell'attività nella prima posizione disponibile (tieni traccia internamente della posizione corrente); se il log è già pieno, la chiamata viene ignorata, quindi il conteggio non supera mai la dimensione del log - Un metodo
getActivityCount()che restituisce il numero di attività registrate - Un metodo
getUsername()che restituisce il nome utente
- membri privati: uno
main.cpp: dimostra il comportamento del distruttore in diversi scenari. Leggi da input un nome utente e la dimensione del log (ciascuno su una riga separata), poi:- Crea un blocco di ambito usando parentesi graffe
{ }e al suo interno crea unaSessionallocata sullo stack con il nome utente inserito, l'ID sessione101e la dimensione del log inserita. Registra due attività con i codici1e2, poi stampa"Stack session activities: <count>". Quando il blocco termina, il distruttore deve essere eseguito automaticamente. - Dopo il blocco, stampa
"Stack session destroyed" - Crea una
Sessionallocata nell'heap usandonew, con il nome utente"Guest", l'ID sessione202e la dimensione del log3. Registra un'attività con il codice5, stampa"Heap session activities: <count>", quindi eliminala esplicitamente condelete. - Dopo l'eliminazione, stampa
"Heap session destroyed"
- Crea un blocco di ambito usando parentesi graffe
Questa sfida mostra i due modi principali in cui vengono chiamati i distruttori: automaticamente quando gli oggetti allocati sullo stack escono dall'ambito e manualmente quando elimini gli oggetti allocati nell'heap. Entrambi i percorsi devono liberare correttamente il log delle attività allocato dinamicamente per evitare perdite di memoria.
Includi il file header in main.cpp usando #include "Session.h".
Provalo tu
#include <iostream>
#include <string>
#include "Session.h"
using namespace std;
int main() {
// Leggi l'input
string username;
int logSize;
getline(cin, username);
cin >> logSize;
// TODO: Crea un blocco di ambito con parentesi graffe { }
// All'interno del blocco:
// - Crea un oggetto Session allocato sullo stack con lo username fornito in input, l'ID di sessione 101 e la dimensione del log fornita in input
// - Registra due attività con i codici 1 e 2
// - Stampa "Stack session activities: <count>"
// Quando il blocco termina, il distruttore viene eseguito automaticamente
// TODO: Dopo il blocco, stampa "Stack session destroyed"
// TODO: Crea un oggetto Session allocato sull'heap usando new
// - Username: "Guest", ID di sessione: 202, dimensione del log: 3
// - Registra un'attività con il codice 5
// - Stampa "Heap session activities: <count>"
// - Elimina la sessione
// TODO: Dopo l'eliminazione, stampa "Heap session destroyed"
return 0;
}
Questa lezione include un breve quiz. Inizia la lezione per rispondere e tenere traccia dei tuoi progressi.
Tutte le lezioni di Programmazione orientata agli oggetti
1Fondamenti della programmazione orientata agli oggetti
File esterniBuild e compilazione in C++File header e file sorgenteNamespace e ambitoIntroduzione alla programmazione orientata agli oggetti in C++Classi e oggetti a confrontoIl puntatore 'this'Metodi (funzioni membro)Attributi (membri dati)Fondamenti di costruttori e distruttoriRiepilogo - Calcolatrice semplice4Proprietà delle classi
Membri di istanza e staticiGetter e setterFunzioni membro constParola chiave mutableMetodi e variabili staticiFunzioni e classi friendRiepilogo - Gestore di conti bancari7Ereditarietà
Ereditarietà di baseLivelli di accesso nell’ereditarietàOrdine di chiamata di costruttori e distruttoriRidefinizione dei metodiFunzioni virtuali e VTableEreditarietà multiplaEreditarietà virtualeRiepilogo - Gerarchia dei dipendenti2Gestione della memoria
Memoria Stack vs HeapPuntatori e riferimentiMemoria dinamica (new/delete)Puntatori intelligenti in C++RAII in C++Riepilogo - Gestore di array dinamico5Incapsulamento
Specificatori di accesso in C++Specificatori di accesso in dettaglioOccultamento delle informazioniStruct vs classClassi annidate e interneRiepilogo - Sistema di registrazione degli studenti8Polimorfismo
Polimorfismo a compile time e a runtimeOverload delle funzioniFunzioni virtuali: ripassoFunzioni virtuali pureClassi astratteProgettazione delle interfacce in C++Dynamic casting e RTTIRipasso: calcolatrice di forme11Concetti avanzati di OOP
Composizione vs ereditarietàMixin tramite CRTPIdiom PimplType ErasureEnum class e tipizzazione forteGestione delle eccezioni in OOPGerarchie personalizzate di eccezioni14Pattern di progettazione - Parte 2
Pattern CommandPattern AdapterPattern DecoratorPattern Template MethodPattern StatePattern CompositeRAII come pattern3Costruttori e distruttori
Costruttore predefinitoCostruttore con parametriCostruttore di copiaCostruttore di spostamentoListe di inizializzazione dei costruttoriCostruttori delegantiApprofondimento sui distruttoriRegola del Tre / Cinque / ZeroRipasso - classe String6Sovraccarico degli operatori
Introduzione al sovraccarico degli operatoriSovraccarico degli operatori aritmeticiSovraccarico degli operatori di confrontoOperatori di flussoSovraccarico dell'operatore di assegnazioneSovraccarico degli operatori [] e ()Operatori di conversione di tipoRipasso - Classe Matrix9Template
Template di funzioneTemplate di classeSpecializzazione dei templateTemplate variadiciBasi di SFINAE e dei trait di tipoRiepilogo - Contenitore genericoEsercitati da solo: Compilatore C++ online