2010-10-14 9 views
6

Nel mio distruttore, devo pulire alcune risorse. Diciamo che ho tre chiamate per cancellare risorse che potrebbero generare. Dal momento che non fa bene lasciare un'eccezione a lasciare un distruttore, quale dovrebbe essere il mio schema di progettazione? Apparentemente la via sottostante non è scalabile.Eccezione nel distruttore

Grazie.

class B::~B(){ 

try{ 
    clearResourceA() 
} 
catch{ 
    try{ 
     clearResourceB(); 
     } 
    catch{ 
     clearResourceC(); 
    } 
    clearResourceC(); 
} 
clearResourceB(); 
    . 
    . 
} 
+0

È * decisamente * non scalabile. Cercando di gestire solo tre risorse, hai già errori logici. – nobar

risposta

2

cattura tutto ciò che può buttare in distruttore con un catch-all (vale a dire, catch (...)) e fate del vostro meglio per gestire le eccezioni generate. Assicurati che nessuna eccezione si propaghi fuori dal distruttore, che il catch-all ti aiuterà a prevenire.

10

Perché non:

try{clearResourceA();} catch(...){} 
try{clearResourceB();} catch(...){} 
try{clearResourceC();} catch(...){} 
0
try 
{ 
    ClearResourceA(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
try 
{ 
    ClearResourceB(); 
} 
catch(...) 
{ 
    ExceptionWasThrown(); 
} 
... 
+0

Offtop: come posso applicare il rientro? –

+0

Utilizza il pulsante "codice", non i segni di spunta posteriori. Il pulsante codice ha un 101010 su di esso. Digitare il codice (rientrato), evidenziare il codice, quindi premere il tasto codice. – Starkey

+0

@Starkey: Sfortunatamente oggi non ci sono né quei pulsanti né l'anteprima. Ho chiesto su meta ma ancora in attesa di risposta –

5

incapsulare ogni risorsa in una classe che li cancella nel suo distruttore (con un try/catch circostante):

struct ProperlyManagedA { 
    // some means of using the resource - a rudimentary way is this: 
    A &getA() { return a; } 
    const A &getA() const { return a; } 
    // cleanup 
    ~ProperlyManagedA() { 
     try { 
      a.clear(); // whatever it is ClearResourceA actually does 
     } catch (...) {} 
    } 
    private: 
    A a; 
} 

Un shared_ptr con un costume deleter è un modo per ottenere ciò senza dover creare un'intera classe per ogni tipo di risorsa.

È possibile migliorare lo scartamento dell'eccezione (ad esempio, registrare il problema), a seconda di ciò che viene generato.

Ancora meglio, modificare le risorse A, B e C in modo che si cancellino nei propri distruttori. Questo potrebbe non essere possibile, però.

In entrambi i casi, è possibile inserire tutte le risorse in una singola classe come desiderato, senza aggiungere alcun codice al distruttore della classe. Questa è "scalabilità". L'intero punto di RAII è che ogni utente di una risorsa non dovrebbe dover scrivere il codice di pulizia per utilizzare correttamente la risorsa.

+1

Ben ordinata. Lo scopo di RAII è che la pulizia venga eseguita nei distruttori in modo da non chiamare delete o freeHandle o qualsiasi altra funzione all'interno dei corpi del codice. Il problema principale qui è che il codice di pulizia in generale non dovrebbe gettare il primo posto. – CashCow

+0

@CashCow: "la pulizia è tutta fatta nei tuoi distruttori" - beh, se le risorse sono progettate con i principi RAII, la pulizia viene eseguita nel distruttore della risorsa. Se devo scrivere io stesso la classe RAII, allora ammetto che è il mio distruttore. Ma chiunque scriva la risorsa RAII, gli utenti di quella risorsa RAII non devono scrivere o chiamare esplicitamente il codice di pulizia. Questo è ciò che intendevo per "utenti". –

+0

grazie, stavo impazzendo guardando gli altri suggerimenti:/ –

2

È inoltre possibile avvolgere le funzioni ClearResources per assicurarsi che non vengano mai lanciate. Perché potrebbero lanciare comunque?

+0

Alcuni tipi di flush (o altri I/O) sono la solita ragione. –

1

Perché hai chiesto informazioni sul modello di progettazione Dirò quale regola empirica utilizzo con successo. Tutte le funzioni che dispongono di funzionalità di pulitura/interruzione devono non rilasciare MAI.

nomi Tali funzioni sono in genere ben riconosciuti:

  • Cancella *()
  • Clean *()
  • Terminate *()
  • Destroy *()
  • uscita *()
  • Detach *()
  • gratuito *()
  • Cancella *()
  • ~ Destructor()
  • ...

La logica che sta alla base di questo è che:

  • ci deve almeno 1 funzione per risorsa che garanzie quella risorsa specifica viene cancellata
  • (ricorsivamente) se si costruisce pulita- attiva la funzione e rilascerai più risorse, quindi utilizza solo le funzioni che garantiscono che tali risorse verranno rilasciate in modo eccezionalmente sicuro
  • programmatori che utilizzano yo ur codice deve avere modo per ripulire-up delle risorse
  • se si esporta funzioni di clean-up al di fuori della libreria, allora non si propagano eccezione (perché gli utenti della biblioteca non sapranno se risorsa viene rilasciato)

E io è possibile provare a utilizzare RAII pattern. Ma anche in questo caso saranno in vigore le regole di cui sopra.

Problemi correlati