RAII in C++
Fa parte della sezione Programmazione orientata agli oggetti del percorso C++ di Coddy. Lezione 16 di 104.
RAII (Resource Acquisition Is Initialization) è un idioma fondamentale del C++ in cui la gestione delle risorse è legata alla durata dell'oggetto. L'idea è semplice: acquisisci le risorse nel costruttore e rilasciale nel distruttore.
Quando viene creato un oggetto, acquisisce le risorse di cui ha bisogno. Quando l'oggetto esce dall'ambito, il suo distruttore rilascia automaticamente tali risorse. Questo garantisce la pulizia anche se si verificano eccezioni.
class FileHandler {
std::FILE* file;
public:
FileHandler(const char* filename) {
file = std::fopen(filename, "r"); // Acquisisci la risorsa
}
~FileHandler() {
if (file) std::fclose(file); // Rilascia la risorsa
}
};Con RAII, non dimentichi mai di eseguire la pulizia:
void processFile() {
FileHandler handler("data.txt"); // Risorsa acquisita
// Lavora con il file...
// Se qui viene lanciata un'eccezione, il distruttore viene comunque eseguito!
} // Risorsa rilasciata automaticamenteI puntatori intelligenti sono l'esempio perfetto di RAII in azione. Un unique_ptr acquisisce la proprietà della memoria heap nel suo costruttore e la elimina nel suo distruttore. Hai già utilizzato RAII senza rendertene conto!
RAII si applica a qualsiasi risorsa: memoria, handle di file, connessioni di rete, mutex o connessioni al database. Incapsulando le risorse negli oggetti, C++ garantisce una pulizia deterministica ed elimina intere categorie di bug, come le perdite di memoria e gli handle pendenti.
Sfida
FacileCostruiamo un gestore di connessioni che dimostra il RAII gestendo automaticamente il ciclo di vita di connessioni simili a quelle di rete. Quando viene creata una connessione, dovrebbe "aprirsi" e, quando esce dall'ambito, dovrebbe "chiudersi" automaticamente: non serve alcuna pulizia manuale!
Creerai due file per organizzare il codice:
Connection.h: definisci una classeConnectionche gestisce una risorsa di connessione usando i principi RAII. La classe dovrebbe avere:- Un attributo privato
connectionId(stringa) per identificare la connessione - Un attributo privato
isOpen(bool) per tenere traccia dello stato della connessione - Un costruttore che accetta un ID di connessione, imposta
isOpensu true e stampa"Opening connection: <id>" - Un distruttore che controlla se la connessione è aperta, la chiude impostando
isOpensu false e stampa"Closing connection: <id>" - Un metodo
send(string message)che stampa"[<id>] Sending: <message>"se la connessione è aperta - Un metodo
getId()che restituisce l'ID della connessione
- Un attributo privato
main.cpp: dimostra come il RAII garantisce la pulizia automatica leggendo dall'input un ID di connessione e un messaggio. Poi:- Crea un ambito usando le parentesi graffe
{ } - All'interno di quell'ambito, crea sullo stack un oggetto
Connectioncon l'ID di input - Usa la connessione per inviare il messaggio di input
- Lascia terminare l'ambito (la connessione dovrebbe chiudersi automaticamente)
- Dopo l'ambito, stampa
"Connection out of scope"
- Crea un ambito usando le parentesi graffe
Il bello del RAII è che non devi ricordarti di chiudere la connessione: il distruttore se ne occupa automaticamente quando l'oggetto esce dall'ambito. Questo schema previene le perdite di risorse anche se nel codice succede qualcosa di imprevisto.
Includi il file header in main.cpp usando #include "Connection.h".
Provalo tu
#include <iostream>
#include <string>
#include "Connection.h"
using namespace std;
int main() {
string connectionId;
string message;
// Leggi l'input
cin >> connectionId;
cin.ignore();
getline(cin, message);
// TODO: Crea uno scope usando le parentesi graffe { }
// All'interno dello scope:
// - Crea un oggetto Connection sullo stack con connectionId
// - Usa la connessione per inviare il messaggio
// Lascia terminare lo scope (il distruttore verrà chiamato automaticamente)
// TODO: Dopo la fine dello scope, stampa "Connection out of scope"
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