2009-07-27 4 views
10

So che I genera eccezioni da un distruttore.Destructor che chiama una funzione che può generare un'eccezione in C++

Se il mio distruttore chiama una funzione che può generare un'eccezione, va bene se la prendo nel distruttore e non la butto oltre? Oppure può causare l'interruzione in ogni caso e non dovrei chiamare tali funzioni da un distruttore?

+0

Solo per chiarimenti, stai chiedendo se è ok se il distruttore cattura l'eccezione, quindi non lascia mai il distruttore, o se è ok lasciare che lasci il distruttore finché viene catturato fuori? – jalf

+0

Sto chiedendo se è OK se l'eccezione rimane nel d'tor. –

risposta

19

Sì, è legale. Un'eccezione non deve escape dal distruttore, ma qualunque cosa accada all'interno del distruttore o nelle funzioni che chiama, dipende da voi.

(Tecnicamente, un'eccezione può sfuggire da una chiamata distruttore pure. Se ciò accade durante la pila svolgimento perché un'altra eccezione è stata gettata, std::terminate è chiamato. Così è ben definito dallo standard, ma è un davvero cattiva idea.)

+2

Ci scusiamo per il pignolo, ma vorrei usare un termine diverso da "legale". Anche lanciare un'eccezione nel distruttore è "legale", i. e. si compilerà e correrà. Ma è una cattiva pratica che causerà effetti spiacevoli. – Dima

+0

Sì e no. Hai ragione è tecnicamente legale lanciare un'eccezione da un distruttore (in questo caso viene chiamato 'std :: terminate'). Ma la tua definizione di "legale" è sbagliata. Solo perché qualcosa compila e gira in nessun modo rende legale C++. – jalf

+1

Non sono sicuro di cosa vuoi dire con certezza, ma sono con Dima su questo.Sebbene C++ non utilizzi il lavoro legale, definisce un * programma ben formato * (probabilmente il più vicino a ciò che ritengo sia "legale"), * comportamento non specificato * e * comportamento non definito *. Consentire a un'eccezione di propagarsi da un distruttore può avvenire in un programma ben strutturato e il comportamento non è né indefinito, né non specificato. Ciò non gli impedisce di essere un comportamento quasi universalmente indesiderabile. –

-1

È possibile trovare this page da C++ FAQ Lite per essere informativo. La risposta di base è "non farlo". Dove esattamente pensi di cogliere questa eccezione? Qualunque cosa tu intenda fare quando rilevi quell'eccezione, fallo semplicemente con una chiamata di funzione o qualcosa del genere (ad esempio registrala o imposta un flag per avvisare l'utente o qualsiasi altra cosa). Lanciare le eccezioni dal distruttore comporta il rischio di far terminare l'intero programma.

+0

Com'è pertinente alla domanda? Se chiama una funzione che può lanciare std :: bad_alloc, quale sarebbe l'alternativa? Scrivi una funzione "wrapper"? Questa non è una soluzione, che nasconde l'eccezione in un altro livello. – MSalters

0

Risposta semplice, non consentire mai un'eccezione da un operatore!

La risposta complicata. Si ottiene veramente inchiodati se l'eccezione sfugge al dtor mentre è attiva un'altra eccezione. Il caso normale è quando stai già srotolando lo stack da un'altra eccezione e l'oggetto in questione viene distrutto. In tal caso, se l'eccezione sfugge all'operatore allora viene chiamato std::terminate, si noti che è possibile inserire il proprio gestore per std::terminate chiamando std::set_terminate. L'implementazione predefinita di std::terminate consiste nel chiamare l'interruzione.

A complicare le cose più, la maggior parte delle funzioni che vogliono fare alcuna garanzia circa la loro sicurezza rispetto alle eccezioni, soprattutto la garanzia di base o la garanzia forte, si basano sui tipi sottostanti a se stessi di non gettare nella loro dtor *

Il vero domanda è, in che stato sarebbe il tuo programma quando questo errore si verifica? Come puoi recuperare? Dove dovrebbe essere gestita questa ripresa? È necessario esaminare il caso specifico e risolvere questi problemi. A volte va bene catturare l'eccezione e ignorarla. Altre volte è necessario alzare alcune bandiere rosse.

Quindi la risposta è: è consentito dal C++ di generare un'eccezione in un dtor, ma non si dovrebbe mai permettere che scappi.

* Ecco una breve synopsis delle garanzie di eccezione (qui c'è un molto più lungo article)

  1. Recap: brevemente definire le garanzie Abrahams sicurezza rispetto alle eccezioni (di base, forti, e nothrow).

La garanzia di base è che fallito operazioni possono alterare stato di programma ma si verificano perdite e colpiti oggetti/moduli sono ancora distruttibili ed utilizzabile, in uno stato coerente (ma non necessariamente prevedibile).

La garanzia forte comporta transazionale commit/rollback semantica: falliti operazioni garantiscano stato programma è invariato rispetto agli oggetti operati. Ciò significa che nessun effetto collaterale influisce sugli oggetti, compresa la validità o il contenuto di degli oggetti helper correlati come gli iteratori che puntano nei contenitori manipolati.

La garanzia di nothrow significa che le operazioni non riuscite non si verificano. L'operazione non genererà un'eccezione.

+0

come ho capito la domanda, non sta chiedendo eccezioni lasciando il dtor a tutti, ma semplicemente se è ok per il dtor chiamare una funzione che genera un'eccezione, purché il dtor catturi l'eccezione in modo che non si propaghi – jalf

3

Sì.

Esaminare la classe std :: fstream nella libreria standard per un esempio.

  • close() potrebbe potenzialmente generare un'eccezione.
  • Il destroctor può chiamare close() ma il distruttore non lancia (annulla eventuali eccezioni).

Il concetto è che se il distruttore chiama qualsiasi metodo che può lanciare allora questi metodi dovrebbero essere pubblici. Pertanto, se l'utente dell'oggetto desidera controllare le eccezioni, può utilizzare i metodi pubblici e gestire l'eccezione. Se non si preoccupano dell'eccezione, lascia che sia il distruttore a gestire il problema.

Tornando all'esempio std :: fstream.

{ 
    std::fstream text("Plop"); 
    // Load Text. 

    // I don't care if the close fails. 
    // So let the destructor handle it and discard exceptions 
} 



{ 
    // If this fails to write I should at least warn the user. 
    // So in this case I will explicitly try and close it. 
    try 
    { 
     std::ofstram password("/etc/password"); 
     // Update the password file. 

     password.close(); 
    } 
    catch(...) 
    { 
      Message.ShowDialog("You failed to update the Password File"); 
    } 
} 
1

Potete trovare alcuni esempi qui: https://software.intel.com/sites/products/documentation/doclib/iss/2013/sa-ptr/sa-ptr_win_lin/GUID-D2983B74-74E9-4868-90E0-D65A80F8F69F.htm

Se un'eccezione lascia distruttore durante la pila svolgimento di un'altra eccezione la propagazione, quindi std :: terminate() è chiamato.

Se non è in corso lo srotolamento dello stack, un'eccezione può lasciare il distruttore senza che std :: terminate() venga chiamato. Tuttavia, per gli oggetti allocati nell'heap ciò causerà una perdita di memoria perché "operator delete" non verrà chiamato per l'oggetto che lancia un'eccezione dal suo distruttore. Sorprendentemente, il distruttore della classe base viene ancora chiamato in questo caso: What happens to base class destructor if a derived class destructor throws an exception

Se l'eccezione viene catturata all'interno del distruttore (in modo che l'eccezione non lasci il distruttore), quindi nessun problema, anche se lo stacco che si svolge di un'altra eccezione è in corso. Questo caso è descritto più approfonditamente qui: http://bin-login.name/ftp/pub/docs/programming_languages/cpp/cffective_cpp/MEC/MI11_FR.HTM