lancia una nuova eccezione std :: vs lancia un'eccezione std ::


113

mentre guardavo del codice mi sono imbattuto in:

throw /*-->*/new std::exception ("//...

e ho sempre pensato che non hai bisogno / non dovresti usare newqui.
Qual è il modo corretto, sono entrambi OK, in caso affermativo c'è qualche differenza?

A proposito, da quello che posso vedere mentre "grepping" con PowerShell boost le librerie non usano mai throw new.

PS inoltre ho trovato del codice CLI che usa throw gcnew. È ok?


1
Penso che throw gcnewsarebbe utile ad es. se vuoi che il codice gestito catturi la tua eccezione. Qualcuno può correggermi su questo?
jpalecek

1
.Net si occupa delle eccezioni tramite puntatore, quindi lanciare gcnew è la cosa giusta da fare lì.
Sebastian Redl

1
@SebastianRedl. Net "puntatore" può essere ambiguo? Anche se gcnew certamente non lo è. System::Exceptionè generalmente un riferimento a un oggetto gestito nell'heap di Garbage Collection. Ho sempre lanciato gcnewe preso con System::Exception ^. Ovviamente uso finallysempre anche in C ++ / CLI, anche se spesso non si mescolano con le eccezioni C ++ nello stesso tryblocco, non sono sicuro del perché.

Risposte:


89

Il modo convenzionale per generare e catturare eccezioni è lanciare un oggetto eccezione e catturarlo per riferimento (di solito constriferimento). Il linguaggio C ++ richiede che il compilatore generi il codice appropriato per costruire l'oggetto eccezione e per pulirlo adeguatamente al momento opportuno.

Lanciare un puntatore a un oggetto allocato dinamicamente non è mai una buona idea. Le eccezioni dovrebbero consentire di scrivere un codice più robusto di fronte a condizioni di errore. Se lanci un oggetto eccezione nel modo convenzionale, puoi essere certo che se viene catturato da una clausola catch che nomina il tipo corretto, da a catch (...), se viene rilanciato o meno, verrà distrutto correttamente al momento opportuno. (L'unica eccezione è se non viene mai catturato, ma questa è una situazione non recuperabile in qualunque modo la si guardi.)

Se lanci un puntatore a un oggetto allocato dinamicamente devi essere sicuro che qualunque sia l'aspetto dello stack di chiamate nel punto in cui vuoi lanciare la tua eccezione, ci sia un blocco catch che nomina il tipo di puntatore corretto e ha la deletechiamata appropriata . La tua eccezione non deve mai essere catturata a catch (...)meno che quel blocco non rilanci l'eccezione che viene quindi catturata da un altro blocco catch che gestisce correttamente l'eccezione.

In effetti, questo significa che hai preso la funzione di gestione delle eccezioni che dovrebbe rendere più facile scrivere codice robusto e ha reso molto difficile scrivere codice corretto in tutte le situazioni. Questo lascia da parte il problema che sarà quasi impossibile agire come codice di libreria per il codice client che non si aspetta questa funzionalità.


1
"lancia un oggetto eccezione" impila o accumula il mio amico? Stack o mucchio? (Forse stavo guardando un cattivo esempio globale da qualche parte) oh e se stack, allora qual è l'ambito appropriato?

@ebyrob: Non sono proprio sicuro di cosa stai chiedendo, ma sembra che tu voglia sapere dell'archiviazione e / o della durata dell'oggetto eccezione a cui potrebbe essere data risposta qui . In caso contrario, potrebbe essere meglio fare una domanda separata.
CB Bailey

31

Non è necessario utilizzarlo newquando si lancia un'eccezione.

Scrivi e basta:

throw yourexception(yourmessage);

e prendi come:

catch(yourexception const & e)
{
      //your code (probably logging related code)
}

Nota che yourexceptiondovrebbe derivare da std::exceptiondirettamente o indirettamente.


7
Perché? perché non usare new? perché derivare yourexceptionda std::exception?
Walter

Quando sono pigro (il che è mooolto spesso) perché non throw std::exception;funziona? g ++ non sembra compilarlo ...

7
@ebyrob: std::exceptionè un tipo e non puoi lanciare un tipo , devi lanciare un oggetto . Quindi la sintassi dovrebbe essere questa: throw std::exception();che verrà compilato. Ora quanto è buono, è una questione completamente diversa.
Nawaz

22

Il lancio new std::exceptionè corretto se il sito della chiamata si aspetta di catturare un file std::exception*. Ma nessuno si aspetterà di rilevare un puntatore a un'eccezione. Anche se documenti questo è ciò che fa la tua funzione e le persone leggono la documentazione, potrebbero comunque dimenticarlo e provare invece a catturare un riferimento a un std::exceptionoggetto.


27
Il lancio new std::exceptionè corretto solo se il sito di chiamata si aspetta di catturare un puntatore E si aspetta di assumere la gestione dell'eccezione di allocazione E non ci saranno mai casi in cui la tua funzione verrà chiamata da qualcosa che non cattura esplicitamente il puntatore corretto ( catch(...)o nessuna manipolazione) altrimenti ci sarà una perdita di oggetto. In breve, questo può essere approssimato come "mai".
CB Bailey

È curioso come questa risposta sia stata accettata, quando in realtà è il commento di @ CharlesBailey ad essere la risposta corretta.
John Dibling

@ John: Anche questo mi è passato per la mente. Ma penso che l'uno-due abbia un buon effetto con me che fornisco il riassunto secco e Charles che espande in modo divertente i vari modi in cui le persone possono dimenticare di affrontarlo correttamente. Peccato però che non si ottenga reputazione grazie ai commenti più votati.

Charles non ha fornito la sua risposta, e questo A (a differenza dell'altro) ha spiegazioni sia nella A che nel commento.
NoSenseEtAl

9

Le FAQ di C ++ hanno una bella discussione su questo:

  1. https://isocpp.org/wiki/faq/exceptions#what-to-catch
  2. https://isocpp.org/wiki/faq/exceptions#catch-by-ptr-in-mfc

Fondamentalmente "a meno che non ci sia una buona ragione per non farlo, catturare per riferimento. Evita di catturare per valore, poiché ciò causa la creazione di una copia e la copia può avere un comportamento diverso da quello che è stato lanciato. Solo in circostanze molto speciali dovresti catturare per puntatore. "


2
Come al solito la FAQ è formulata male. Puoi catturare per valore o riferimento. Un puntatore è semplicemente un valore (che si cattura per valore o riferimento). Ricorda che il tipo Aè distinto dal tipo, A*quindi se lo faccio throw A()NON posso prenderlo catch(A* e)in quanto è di tipo completamente diverso.
Martin York

Questi collegamenti sono ora interrotti.
stephenspann

1
Ho corretto i link @spanndemic
user1202136

1

L'operatore new non può garantire che non solleverà mai un'eccezione. Per questo motivo usarlo per lanciare un'eccezione "valida" (prevista) produrrebbe un codice che non può essere garantito per non andare in crash. Poiché può esserci una sola eccezione alla volta e il programma tenta di lanciarne due prima che una di esse possa essere catturata, la cosa migliore che un'implementazione può fare è interrompere immediatamente il programma, ad esempio chiamando std :: terminate.

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.