abortire, terminare o uscire?


112

Qual è la differenza tra questi tre e come devo terminare il programma in caso di eccezione che non riesco a gestire correttamente?


3
Questo non è un duplicato, ma piuttosto un sottoinsieme con alcune buone risposte stackoverflow.com/questions/397075/… ed è stato anche taggato C ++!
Ellie Kesselman

std::abortè ragionevole se un'eccezione non può essere risolta in un distruttore.
Daniel

1
Per ulteriori informazioni su std::terminatevedere questi articoli nell'eccellente blog C ++ di Andrzej: akrzemi1.wordpress.com/2011/09/28/who-calls-stdterminate , akrzemi1.wordpress.com/2011/10/05/using-stdterminate
Ohad Schneider

Risposte:


3

Il mio consiglio sarebbe di non usarne nessuno. Invece, catchle eccezioni che non puoi gestire main()e semplicemente returnda lì. Ciò significa che hai la garanzia che lo svolgimento dello stack avvenga correttamente e che tutti i distruttori vengano chiamati. In altre parole:

int main() {
    try {
       // your stuff
    }
    catch( ... ) {
       return 1;    // or whatever
    }
}

8
@ Neil: Fondamentalmente d'accordo, ma le eccezioni che il programma non può gestire dovrebbero essere segnalate e rigettate. Lascia che l'app si blocchi.
John Dibling

13
Per assicurarti che la pila si srotoli dovresti sempre prendere in main. Ma vorrei rilanciare dalla cattura. Poiché alcuni sistemi operativi hanno la capacità di invocare automaticamente l'infrastruttura di debug se hai compilato in debug.
Martin York

5
Le eccezioni che non vengono rilevate nemmeno da un gestore di primo livello potrebbero richiamare una funzione di segnalazione del sistema che scarica il processo e carica il rapporto sulle eccezioni per l'attenzione degli sviluppatori, come Segnalazione errori di Windows, rapporti sugli errori di Mac OS X e registri degli errori dell'applicazione iPhone.
JBR Wilkinson

6
@ John Il motivo per cui mi sorprende è che, sebbene abbia perfettamente senso alla luce di come le eccezioni sono effettivamente implementate, interrompe l'astrazione secondo cui un'eccezione "si propaga in alto nello stack" finché non viene trovato un gestore adatto (o viene terminato chiamato). E le astrazioni che perdono, sebbene spesso inevitabili, sono necessariamente sorprendenti quando si incontrano.
Tyler McHenry

11
-1 perché questo non risponde a metà della domanda. "Qual è la differenza tra [abort, terminate o exit?]" Questa è una risposta migliore: stackoverflow.com/a/397081/353094 Anche stackoverflow.com/a/2820407/353094 è un'ottima risposta.
leetNightshade

149
  • abort indica una fine "anormale" del programma e solleva il segnale POSIX SIGABRT, il che significa che verrà richiamato qualsiasi gestore registrato per quel segnale, sebbene il programma terminerà comunque le afterwords in entrambi i casi. Di solito si usa abortin un programma C per uscire da un caso di errore imprevisto in cui è probabile che l'errore sia un bug nel programma, piuttosto che qualcosa come un input errato o un errore di rete. Ad esempio, è possibile abortche una struttura dati contenga un puntatore NULL quando logicamente non dovrebbe mai accadere.

  • exit indica una fine "normale" del programma, sebbene ciò possa comunque indicare un errore (ma non un bug). In altre parole, potresti avere exitun codice di errore se l'utente ha fornito un input che non può essere analizzato o un file non può essere letto. Un codice di uscita 0 indica il successo. exitopzionalmente chiama anche i gestori prima che termini il programma. Questi sono registrati con le funzioni atexite on_exit.

  • std :: terminate è ciò che viene chiamato automaticamente in un programma C ++ quando c'è un'eccezione non gestita. Questo è essenzialmente l'equivalente C ++ di abort, supponendo che tu stia segnalando tutti i tuoi errori eccezionali mediante il lancio di eccezioni. Questo chiama un gestore che è impostato dalla std::set_terminatefunzione, che per impostazione predefinita chiama semplicemente abort.

In C ++, di solito si vuole evitare di chiamare aborto exitin caso di errore, poiché è meglio lanciare un'eccezione e lasciare che il codice più in alto nello stack di chiamate decida se terminare il programma è appropriato o meno. Che tu lo usi o meno exitper il successo è una questione di circostanze, indipendentemente dal fatto che abbia senso terminare il programma in un punto diverso dall'istruzione return in main.

std::terminatedovrebbe essere considerato un ultimo strumento di segnalazione degli errori, anche in C ++. Il problema std::terminateè che il gestore di terminazione non ha accesso all'eccezione che non è stata gestita, quindi non c'è modo di dire cosa fosse. Di solito è molto meglio avvolgere l'intera parte principale in un try { } catch (std::exception& ex) { }blocco. Almeno allora puoi riportare più informazioni sulle eccezioni che derivano da std::exception(anche se ovviamente le eccezioni che non derivano da std::exceptionfinirebbero comunque per non essere gestite).

Racchiudere il corpo di mainin try { } catch(...) { }non è molto meglio che impostare un gestore di terminazione, perché ancora una volta non hai accesso all'eccezione in questione. Modifica: secondo la risposta di Neil Butterworth, c'è un vantaggio nel fatto che lo stack è sciolto in questo caso, il che (in qualche modo sorprendentemente) non è vero per un'eccezione non gestita.


10
Puoi aggiornare questa risposta con le informazioni di C ++ 11? Sembra che ora ci siano modi per ottenere l'eccezione nel catch (...) e nel terminate handler.
Klaim

1
In C ++ gestore sospendere fa avere accesso al l'eccezione attraverso std::current_exception(). Vedi esempio qui: akrzemi1.wordpress.com/2011/10/05/using-stdterminate
anorm

Non importa che tu possa ottenere l'eccezione corrente, dal momento che non puoi ispezionarla. Tutto quello che puoi fare è rilanciarlo.
seattlecpp

2
@seattlecpp puoi rilanciarlo e prendere un riferimento ad esso, che puoi quindi ispezionare
gpeche

16

std :: abort e std :: exit (e altre: std :: _ Exit, std :: quick_exit) sono solo funzioni di livello inferiore. Li usi per dire al programma cosa vuoi che faccia esattamente: quali distruttori (e se) chiamare, quali altre funzioni di pulizia chiamare, quale valore restituire, ecc.

std :: terminate è un'astrazione di livello superiore: viene chiamato (da run-time o da te) per indicare che si è verificato un errore nel programma e che per qualche motivo non è possibile gestirlo lanciando un'eccezione. La necessità di ciò si verifica in genere quando si verifica un errore nel meccanismo di eccezione stesso, ma è possibile utilizzarlo in qualsiasi momento quando non si desidera che il programma continui oltre l'errore specificato. Ho compilato l'elenco completo delle situazioni in cui std :: terminate viene chiamato nel mio post. Non è specificato cosa fa std :: terminate, perché ne hai il controllo. È possibile configurare il comportamento registrando qualsiasi funzione. I limiti che hai sono che la funzione non può tornare al sito di errore e non può uscire tramite un'eccezione, ma tecnicamente puoi persino avviare il tuo message pump all'interno. Per l'elenco delle cose utili che puoi fare all'interno, vedi il mio altro post .

In particolare, nota che std :: terminate è considerato un gestore di eccezioni nei contesti in cui std :: terminate viene chiamato a causa di un'eccezione generata che non può essere gestita, e puoi controllare quale fosse l'eccezione e ispezionarla usando C ++ 11 utilizzando std :: rethrow_exception e std :: current_exception. È tutto nel mio post .


È consigliabile disporre di un gestore di pulizia nel caso in cui il programma si interrompa a causa dei segnali di sistema? Ad esempio, un accesso non valido alla memoria porta alla generazione del segnale SIGSEGV. In questo caso, è bene lasciare che il programma termini e avere il file core o registrare un gestore di segnali per eseguire la pulizia ?? Ci sono preoccupazioni per la pulizia durante la gestione dei segnali di sistema rispetto alla pulizia durante la gestione di std :: terminate?
kartik trivikram

12

quick_exit () !

Se il tuo programma è multi-thread, la chiamata exit()molto probabilmente causerà un arresto anomalo perché si std::threadtenterà di distruggere oggetti globali / statici senza uscire dai loro thread.

Se si desidera restituire un codice di errore e uscire dal programma (più o meno) normalmente, chiamare i quick_exit()programmi multi-thread. Per una terminazione anomala (senza possibilità di specificare il codice di errore) abort()o std::terminate()può essere chiamato.

Nota: quick_exit () non è stato supportato da MSVC ++ fino alla versione 2015.


4
  • terminate ti lascia la possibilità di registrare cosa accadrà quando verrà chiamato. Dovrebbe essere uno degli altri due.
  • exit è una normale uscita che consente di specificare uno stato di uscita. Gli handler registrati da at_exit () vengono eseguiti
  • l'aborto è un'uscita anormale. L'unica cosa che viene eseguita è il gestore del segnale per SIGABRT.

4
  • terminate () viene chiamato automaticamente quando si verifica un'eccezione che non può essere gestita. Per impostazione predefinita, terminate () chiama abort (). È possibile impostare un handle personalizzato con la funzione set_terminate ().

    abort () invia il segnale SIGABRT.

    exit () non è necessariamente una cosa negativa. Esce con successo dall'applicazione e chiama le funzioni atexit () nell'ordine LIFO. Normalmente non lo vedo nelle applicazioni C ++, tuttavia, lo vedo in molte applicazioni basate su unix dove invia un codice di uscita alla fine. Di solito un'uscita (0) indica una corretta esecuzione dell'applicazione.


8
Senza esito! Sia in Unix che in DOS, exit (0) indica successo e qualsiasi altro valore passato a exit () indica fallimento, non il contrario!
Richard Barrell
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.