Che cos'è RAII? Esempi?


19

Sempre quando viene usato il termine RAII, le persone in realtà parlano di decostruzione anziché di inizializzazione. Penso di avere una comprensione di base di cosa potrebbe significare, ma non ne sono del tutto sicuro. Inoltre: il C ++ è l'unico linguaggio RAII? Che dire di Java o C # /. NET?

Risposte:


25

L'acquisizione delle risorse è l'inizializzazione significa che gli oggetti dovrebbero occuparsi di se stessi come un pacchetto completo e non aspettarsi che altri codici dicano a un'istanza "ehi, comunque, verrai ripulito presto - ti preghiamo di sistemare ora". Di solito significa che c'è qualcosa di significativo nel distruttore. Significa anche che scrivi una classe appositamente per gestire le risorse, sapendo che in determinate circostanze difficili da prevedere, come ad esempio le eccezioni, puoi contare sull'esecuzione dei distruttori.

Supponi di voler scrivere un po 'di codice in cui cambi il cursore di Windows in un cursore di attesa (clessidra, ciambella non funzionante, ecc.), Fai le tue cose e poi ripristinalo. E dire anche che "fai le tue cose" potrebbe generare un'eccezione. Il modo in cui RAII lo farebbe sarebbe quello di fare in modo che una classe il cui ctor mettesse il cursore in attesa, il cui unico metodo "reale" facesse qualunque cosa tu volessi fare, e il cui dtor riportasse il cursore indietro. Le risorse (in questo caso lo stato del cursore) sono legate all'ambito di un oggetto. Quando si acquisisce la risorsa, si inizializza un oggetto. Puoi contare sull'oggetto che viene distrutto se vengono generate eccezioni e ciò significa che puoi contare su come ripulire la risorsa.

Usare bene RAII significa che non è necessario finally. Naturalmente, si basa sulla distruzione deterministica, che non si può avere in Java. Puoi ottenere una sorta di distruzione deterministica in C # e VB.NET con using.


4
Penso che questo sia ciò a cui stai arrivando, ma potresti voler aggiungere che il motivo per cui Java e C # non supportano RAII è a causa del Garbage Collector. In C ++, un oggetto locale verrà distrutto non appena esce dall'ambito. In Java / C # questo non è vero.
Jason Baker,

Espandendosi sul punto Jasons, il motivo per cui Java e C # non possono garantire la distruzione tempestiva è a causa della possibilità di cicli di riferimento, il che significa che è impossibile determinare un ordine sicuro per eseguire i distruttori. I cicli di riferimento possono anche avvenire in C ++, ma le implicazioni sono diverse: il programmatore diventa responsabile della determinazione dell'ordine di distruzione e delle eliminazioni esplicite. Tale responsabilità è spesso impacchettata in alcuni distruttori di classi di livello superiore, ad esempio una classe di container è responsabile di assicurare che tutti gli oggetti contenuti vengano distrutti. La "proprietà" è la chiave.
Steve314,

1
@Jason è quello che intendevo per "distruzione deterministica": un programmatore C ++ sa quando verrà eseguito il distruttore.
Kate Gregory,

So che questa è una vecchia risposta, ma sono ancora un po 'confuso. Ho appena saputo del termine e alcune informazioni dicono che l'acquisizione dovrebbe avvenire nel costruttore. Questo non ha davvero senso per me e questa risposta sembra contraddirla, ma potresti chiarire?
Per Johansson,

1
@PerJohansson Sì, acquisisci nel ctor. E rilasci nel dtor. Mi sono concentrato sul secondo punto, ma vanno insieme. Una volta che il ctor è finito, sai di avere un oggetto valido. E sai che qualunque cosa accada, la risorsa verrà rilasciata al momento giusto.
Kate Gregory,

4

RAII riguarda in parte la decisione quando un oggetto diventa responsabile della propria pulizia - la regola è che l'oggetto diventa responsabile se e quando la sua inizializzazione del costruttore viene completata. La simmetria di inizializzazione e pulizia, costruttore e distruttore, significa che i due hanno stretti legami tra loro.

Un punto di RAII è garantire la sicurezza delle eccezioni - che l'applicazione rimanga autoconsistente quando vengono generate eccezioni. A prima vista questo è banale: quando un'eccezione provoca la chiusura di un ambito, le variabili locali in tale ambito devono essere distrutte.

Ma cosa succede se il lancio dell'eccezione si verifica all'interno di un costruttore?

Bene, l'oggetto non è stato completamente costruito, quindi non può essere distrutto in modo sicuro. Il costruttore dovrebbe aver provato i blocchi necessari per assicurarsi che vengano eseguite tutte le pulizie necessarie prima che l'eccezione venga propagata. Una volta che l'eccezione si propaga al di fuori dell'ambito in cui l'oggetto è stato costruito, non vi sarà alcuna chiamata al distruttore, poiché l'oggetto non è stato costruito in primo luogo.

Considerare in particolare i costruttori per i dati dei membri all'interno dell'oggetto che vengono distrutti. Se uno di questi genera un'eccezione, il tuo codice costruttore principale non verrà eseguito affatto, ma avrà un codice che costituisce una parte implicita di quel costruttore. Tutti i membri che sono stati costruiti con successo verranno automaticamente distrutti. I membri che non sono stati costruiti (incluso quello che ha generato l'eccezione) non lo sono.

Quindi, in sostanza, RAII è una politica che garantisce che tutto ciò che viene completamente costruito verrà distrutto in modo tempestivo, in particolare in presenza di eccezioni, e che qualsiasi oggetto sia completamente costruito o non lo sia (non ci sono metà- oggetti costruiti che non puoi sapere come ripulire in sicurezza). Anche le risorse allocate vengono liberate. E gran parte del lavoro è automatizzato, quindi il programmatore non deve preoccuparsi troppo.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.