2010-02-23 15 views
156

Cosa si intende per Acquisizione risorsa è Inizializzazione (RAII)?Cosa si intende per Acquisizione risorsa è Inizializzazione (RAII)?

+11

http://en.wikipedia.org/wiki/Resource_Acquisition_Is_Initialization –

+8

Questo è ciò che lo guida a casa per me. http://www.stroustrup.com/bs_faq2.html#finally –

+1

Riferimento Microsoft con 3 frasi e 2 esempi ancora molto chiari! https://msdn.microsoft.com/en-us/library/hh438480.aspx –

risposta

85

Si tratta di un linguaggio di programmazione che significa in breve che si

  • incapsulare una risorsa in una classe (il cui costruttore di solito - ma non necessariamente ** - acquisisce la risorsa, e il suo distruttore rilascia sempre)
  • utilizzare la risorsa tramite un'istanza locale della classe *
  • la risorsa viene liberato automaticamente quando l'oggetto esce di portata

Questo garantisce ° a qualunque cosa accada mentre la risorsa è in uso, alla fine verrà liberata (indipendentemente dal ritorno normale, dalla distruzione dell'oggetto contenitore o da un'eccezione generata).

È una buona pratica ampiamente utilizzata in C++, perché oltre ad essere un modo sicuro per gestire le risorse, rende anche il codice molto più pulito in quanto non è necessario combinare il codice di gestione degli errori con la funzionalità principale.

*Aggiornamento: "locale" può significare una variabile locale o una variabile membro non statico di una classe. Nel secondo caso la variabile membro viene inizializzata e distrutta con il suo oggetto proprietario.

**Update2: come sottolineato @sbi, la risorsa, sebbene spesso allocata all'interno del costruttore, può anche essere allocata all'esterno e passata come parametro.

+0

AFAIK, l'acronimo non implica che l'oggetto debba trovarsi su una variabile locale (stack).Potrebbe essere una variabile membro di un altro oggetto, quindi quando l'oggetto "holding" viene distrutto, anche l'oggetto membro viene distrutto e la risorsa viene rilasciata. In effetti, penso che l'acronimo significhi specificamente solo che non ci sono metodi 'open()'/'close()' per inizializzare e rilasciare la risorsa, solo il costruttore e il distruttore, quindi il 'mantenimento' della risorsa è solo la vita dell'oggetto, non importa se tale durata è gestita dal contesto (stack) o esplicitamente (allocazione dinamica) – Javier

+1

In realtà nulla dice che la risorsa deve essere acquisita nel costruttore. File stream, stringhe e altri container lo fanno, ma la risorsa potrebbe anche essere * passata * al costruttore, come avviene di solito con i puntatori intelligenti. Dal momento che la tua è la risposta più votata, potresti voler risolvere questo problema. – sbi

+0

Non è un acronimo, è un'abbreviazione. IIRC la maggior parte delle persone la pronuncia "ar ey ay ay" quindi non si qualifica realmente per un acronimo come DARPA, che è pronunciato DARPA invece che spelling. Inoltre, direi che RAII è un paradigma piuttosto che un semplice idioma. – dtech

37

"RAII" sta per "Acquisizione delle risorse è di inizializzazione" e in realtà è piuttosto un termine improprio, in quanto non è risorsa acquisizione (e l'inizializzazione di un oggetto) che si occupa, ma rilasciando la risorsa (mediante distruzione di un oggetto).
Ma RAII è il nome che abbiamo e si attacca.

Al suo cuore, l'idioma dispone incapsulare risorse (blocchi di memoria, file aperti, mutex sbloccati, you-name-it) in , oggetti automatici locali, e avendo il distruttore di tale oggetto il rilascio del risorsa quando l'oggetto viene distrutto alla fine dell'ambito di appartenenza:

{ 
    raii obj(acquire_resource()); 
    // ... 
} // obj's dtor will call release_resource() 

Naturalmente, oggetti non sono sempre, oggetti automatici locali. Potrebbero essere anche membri di una classe:

class something { 
private: 
    raii obj_; // will live and die with instances of the class 
    // ... 
}; 

Se tali oggetti gestiscono la memoria, vengono spesso chiamati "puntatori intelligenti".

Ci sono molte varianti di questo. Ad esempio, nei primi frammenti di codice sorge la domanda che cosa succederebbe se qualcuno volesse copiare obj. La soluzione più semplice sarebbe semplicemente non consentire la copia. std::unique_ptr<>, un puntatore intelligente per far parte della libreria standard come descritto dal prossimo standard C++, fa questo.
Un altro puntatore intelligente di questo tipo, std::shared_ptr, contiene "proprietà condivisa" della risorsa (un oggetto assegnato dinamicamente) che contiene.Cioè, può essere liberamente copiato e tutte le copie si riferiscono allo stesso oggetto. Il puntatore intelligente tiene traccia di quante copie si riferiscono allo stesso oggetto e lo eliminerà quando l'ultimo viene distrutto.
Una terza variante è rappresentata da std::auto_ptr che implementa un tipo di semantica del movimento: un oggetto è di proprietà di un solo puntatore e il tentativo di copiare un oggetto risulterà (tramite l'hacking della sintassi) nel trasferimento della proprietà dell'oggetto alla destinazione di l'operazione di copia.

+3

'std :: auto_ptr' è la versione obsoleta di' std :: unique_ptr'. 'std :: auto_ptr' tipo di semantica di movimento simulato quanto più possibile in C++ 98,' std :: unique_ptr' usa la nuova semantica di movimento di C++ 11. La nuova classe è stata creata perché la semantica di spostamento di C++ 11 è più esplicita (richiede 'std :: move' tranne che da temporanea) mentre era predefinita per qualsiasi copia da non-const in' std :: auto_ptr'. –

8

Il libro descrive C++ Programming with Design Patterns Revealed RAII come:

  1. L'acquisizione di tutte le risorse
  2. utilizzando le risorse
  3. liberando risorse

Dove

  • risorse sono ESECUZIONE come classi, e tutti i puntatori hanno wrapper di classe intorno a loro (rendendoli puntatori intelligenti).

  • Le risorse vengono acquisite invocando i loro costruttori e rilasciate implicitamente (in ordine inverso di acquisizione) invocando i loro distruttori.

+1

@Brandin Ho modificato il mio post in modo che i lettori si concentrino sul contenuto che conta, piuttosto che discutere l'area grigia della legge sul copyright di ciò che costituisce fair use. – Dennis

237

è un nome davvero terribile per un concetto incredibilmente potente, e forse una delle numero 1 cose che gli sviluppatori C++ perdere quando si passa ad altre lingue. C'è stato un po 'di movimento per provare a rinominare questo concetto come Scope-Bound Resource Management, anche se non sembra essersi ancora imbattuto.

Quando diciamo 'Risorsa' non intendiamo solo memoria: potrebbero essere handle di file, socket di rete, handle di database, oggetti GDI ... In breve, cose di cui abbiamo una fornitura limitata e quindi abbiamo bisogno essere in grado di controllare il loro utilizzo. L'aspetto "Scope-bound" significa che la durata dell'oggetto è legata all'ambito di una variabile, quindi quando la variabile esce dall'ambito, il distruttore rilascerà la risorsa. Una proprietà molto utile di questo è che garantisce una maggiore sicurezza delle eccezioni. Ad esempio, confronta:

RawResourceHandle* handle=createNewResource(); 
handle->performInvalidOperation(); // Oops, throws exception 
... 
deleteResource(handle); // oh dear, never gets called so the resource leaks 

Con l'RAII uno

class ManagedResourceHandle { 
public: 
    ManagedResourceHandle(RawResourceHandle* rawHandle_) : rawHandle(rawHandle_) {}; 
    ~ManagedResourceHandle() {delete rawHandle; } 
    ... // omitted operator*, etc 
private: 
    RawResourceHandle* rawHandle; 
}; 

ManagedResourceHandle handle(createNewResource()); 
handle->performInvalidOperation(); 

In quest'ultimo caso, quando viene generata l'eccezione e lo stack è svolto, le variabili locali vengono distrutte che assicura che la nostra risorsa è pulito e non perde.

+10

Molto chiaro e non solo concettuale. Grazie. –

+59

+1 per "è un nome davvero terribile" – deinocheirus

+0

@the_mandrill: Ho provato a ideone.com/1Jjzuc questo programma. Ma non c'è nessuna chiamata al distruttore. Il tomdalling.com/blog/software-design/... dice che C++ garantisce che venga chiamato il distruttore di oggetti nello stack, anche se viene lanciata un'eccezione. Quindi, perché il distruttore non è stato eseguito qui? La mia risorsa è trapelata o non verrà mai rilasciata o liberata? – Destructor

1

La gestione manuale della memoria è un incubo che i programmatori hanno inventato modi per evitare dall'invenzione del compilatore. La programmazione dei linguaggi con i garbage collector semplifica la vita, ma a scapito delle prestazioni. In questo articolo - Eliminating the Garbage Collector: The RAII Way, l'ingegnere di Toptal Peter Goodspeed-Niklaus ci dà una sbirciatina nella storia dei netturbini e spiega come le nozioni di proprietà e di prestito possono aiutare ad eliminare i netturbini senza compromettere le loro garanzie di sicurezza.

Problemi correlati