Domanda

In C++ è possibile specificare che una funzione può o meno generare un'eccezione utilizzando uno specificatore di eccezione.Per esempio:

void foo() throw(); // guaranteed not to throw an exception
void bar() throw(int); // may throw an exception of type int
void baz() throw(...); // may throw an exception of some unspecified type

Ho dei dubbi sull'effettivo utilizzo a causa di quanto segue:

  1. Il compilatore non applica realmente gli specificatori di eccezione in modo rigoroso, quindi i vantaggi non sono grandi.Idealmente, vorresti ricevere un errore di compilazione.
  2. Se una funzione viola uno specificatore di eccezione, penso che il comportamento standard sia terminare il programma.
  3. In VS.Net, tratta Throw(X) come Throw(...), quindi l'aderenza allo standard non è forte.

Pensi che dovrebbero essere usati specificatori di eccezioni?
Rispondi con "sì" o "no" e fornisci alcune ragioni per giustificare la tua risposta.

È stato utile?

Soluzione

NO.

Ecco alcuni esempi del perché:

  1. Il codice del modello è impossibile da scrivere con specifiche di eccezione,

    template<class T>
    void f( T k )
    {
         T x( k );
         x.x();
    }
    

    Le copie potrebbero generare un'eccezione, il passaggio dei parametri potrebbe generare un'eccezione e x() potrebbe generare qualche eccezione sconosciuta.

  2. Le specifiche delle eccezioni tendono a vietare l'estensibilità.

    virtual void open() throw( FileNotFound );
    

    potrebbe evolversi in

    virtual void open() throw( FileNotFound, SocketNotReady, InterprocessObjectNotImplemented, HardwareUnresponsive );
    

    Potresti davvero scriverlo come

    throw( ... )
    

    Il primo non è estensibile, il secondo è troppo ambizioso e il terzo è proprio ciò che intendi quando scrivi funzioni virtuali.

  3. Codice legacy

    Quando scrivi codice che si basa su un'altra libreria, non sai veramente cosa potrebbe fare se qualcosa va terribilmente storto.

    int lib_f();
    
    void g() throw( k_too_small_exception )
    { 
       int k = lib_f();
       if( k < 0 ) throw k_too_small_exception();
    }
    

    g terminerà, quando lib_f() lancia.Questo (nella maggior parte dei casi) non è quello che vuoi veramente. std::terminate() non dovrebbe mai essere chiamato.È sempre meglio lasciare che l'applicazione si blocchi con un'eccezione non gestita, dalla quale è possibile recuperare uno stack-trace, piuttosto che morire silenziosamente/violentemente.

  4. Scrivi codice che restituisce errori comuni e genera anomalie in occasioni eccezionali.

    Error e = open( "bla.txt" );
    if( e == FileNotFound )
        MessageUser( "File bla.txt not found" );
    if( e == AccessDenied )
        MessageUser( "Failed to open bla.txt, because we don't have read rights ..." );
    if( e != Success )
        MessageUser( "Failed due to some other error, error code = " + itoa( e ) );
    
    try
    {
       std::vector<TObj> k( 1000 );
       // ...
    }
    catch( const bad_alloc& b )
    { 
       MessageUser( "out of memory, exiting process" );
       throw;
    }
    

Tuttavia, quando la tua libreria genera semplicemente le tue eccezioni, puoi utilizzare le specifiche delle eccezioni per dichiarare il tuo intento.

Altri suggerimenti

Evitare le specifiche delle eccezioni in C++.Le ragioni che fornisci nella tua domanda sono un buon inizio per spiegare il perché.

Vedi Herb Sutter "Uno sguardo pragmatico alle specifiche delle eccezioni".

Penso che la convenzione standard tranne (per C++)
Gli specificatori di eccezioni erano un esperimento nello standard C++ che per lo più fallì.
L'eccezione è che l'identificatore no Throw è utile ma dovresti anche aggiungere internamente il blocco try catch appropriato per assicurarti che il codice corrisponda all'identificatore.Herb Sutter ha una pagina sull'argomento. Gotch 82

In aggiunta penso che valga la pena descrivere le Garanzie sulle Eccezioni.

Si tratta fondamentalmente di documentazione su come lo stato di un oggetto è influenzato dalle eccezioni che sfuggono a un metodo su quell'oggetto.Sfortunatamente non vengono applicati o altrimenti menzionati dal compilatore.
Boost ed eccezioni

Garanzie di eccezione

Nessuna garanzia:

Non esiste alcuna garanzia sullo stato dell'oggetto dopo che un'eccezione sfugge a un metodo
In queste situazioni l'oggetto non dovrebbe più essere utilizzato.

Garanzia di base:

In quasi tutte le situazioni questa dovrebbe essere la garanzia minima fornita da un metodo.
Ciò garantisce che lo stato dell'oggetto sia ben definito e possa ancora essere utilizzato in modo coerente.

Forte garanzia:(nota anche come Garanzia sulle transazioni)

Ciò garantisce che il metodo verrà completato con successo
Oppure verrà lanciata un'eccezione e lo stato degli oggetti non cambierà.

Nessuna garanzia di tiro:

Il metodo garantisce che nessuna eccezione possa propagarsi al di fuori del metodo.
Tutti i distruttori dovrebbero fornire questa garanzia.
| NbSe un'eccezione sfugge a un distruttore mentre un'eccezione si sta già propagando
| L'applicazione terminerà

gcc emetterà avvisi quando si violano le specifiche delle eccezioni.Quello che faccio è utilizzare le macro per utilizzare le specifiche delle eccezioni solo in una modalità "lint" compilata espressamente per verificare che le eccezioni siano conformi alla mia documentazione.

L'unico specificatore di eccezione utile è "throw()", come in "non lancia".

NO.Se li usi e viene generata un'eccezione che non hai specificato, dal tuo codice o dal codice chiamato dal tuo codice, il comportamento predefinito è terminare immediatamente il tuo programma.

Inoltre, credo che il loro utilizzo sia stato deprecato nelle attuali bozze dello standard C++0x.

Le specifiche delle eccezioni non sono strumenti meravigliosamente utili in C++.Tuttavia, /is/ possono essere usati bene, se combinati con std::unexpected.

Ciò che faccio in alcuni progetti è scrivere codice con specifiche di eccezione, quindi chiamare set_unexpected() con una funzione che genererà un'eccezione speciale di mia progettazione.Questa eccezione, durante la costruzione, ottiene un backtrace (in modo specifico della piattaforma) ed è derivata da std::bad_exception (per consentirne la propagazione, se lo si desidera).Se provoca una chiamata terminate(), come avviene di solito, il backtrace viene stampato da what() (così come l'eccezione originale che lo ha causato;non è difficile trovarlo) e così ottengo informazioni su dove è stato violato il mio contratto, ad esempio quale eccezione inaspettata della libreria è stata lanciata.

Se lo faccio, non consentirò mai la propagazione delle eccezioni della libreria (eccetto quelle std) e deriverò tutte le mie eccezioni da std::exception.Se una libreria decide di abbandonare, la catturerò e la convertirò nella mia gerarchia, permettendomi di controllare sempre il codice.Le funzioni basate su modelli che chiamano funzioni dipendenti dovrebbero evitare le specifiche delle eccezioni per ovvie ragioni;ma è comunque raro avere un'interfaccia di funzioni basata su modelli con il codice della libreria (e poche librerie utilizzano davvero i modelli in modo utile).

Se stai scrivendo codice che verrà utilizzato da persone che preferiscono guardare la dichiarazione della funzione piuttosto che eventuali commenti attorno ad essa, allora una specifica dirà loro quali eccezioni potrebbero voler catturare.

Altrimenti non trovo particolarmente utile usare altro che throw() per indicare che non genera eccezioni.

Una specifica "throw()" consente al compilatore di eseguire alcune ottimizzazioni durante l'analisi del flusso di codice se sa che la funzione non genererà mai un'eccezione (o almeno promette di non generare mai un'eccezione).Larry Osterman ne parla brevemente qui:

http://blogs.msdn.com/larryosterman/archive/2006/03/22/558390.aspx

Generalmente non utilizzerei gli specificatori di eccezioni.Tuttavia, nei casi in cui qualsiasi altra eccezione dovesse provenire dalla funzione in questione, il programma non sarebbe assolutamente in grado di farlo corretto, allora può essere utile.In tutti i casi, assicurati di documentare chiaramente quali eccezioni potrebbero essere previste da tale funzione.

Sì, il comportamento previsto di un'eccezione non specificata lanciata da una funzione con specificatori di eccezione è chiamare terminate().

Noterò anche che Scott Meyers affronta questo argomento in More Effective C++.I suoi libri Effective C++ e More Effective C++ sono altamente consigliati.

Sì, se ti interessa la documentazione interna.O magari scrivere una libreria che altri utilizzeranno, in modo che possano dire cosa succede senza consultare la documentazione.Lanciare o non lanciare può essere considerato parte dell'API, quasi come il valore restituito.

Sono d'accordo, non sono molto utili per imporre la correttezza dello stile Java nel compilatore, ma è meglio di niente o di commenti casuali.

Possono essere utili per i test unitari in modo che quando si scrivono i test si sappia cosa aspettarsi che la funzione lanci quando fallisce, ma non vi è alcuna applicazione che li circonda nel compilatore.Penso che siano codice aggiuntivo che non è necessario in C++.Qualunque cosa tu scelga, tutto ciò di cui dovresti essere sicuro è che segui lo stesso standard di codifica in tutto il progetto e nei membri del team in modo che il tuo codice rimanga leggibile.

Dall'articolo:

http://www.boost.org/community/exception_safety.html

"È noto per essere impossibile scrivere un contenitore generico sicuro di eccezione." Questa affermazione viene spesso ascoltata con riferimento a un articolo di Tom Cargill [4] in cui esplora il problema della sicurezza delle eccezioni per un modello di stack generico.Nel suo articolo, Cargill solleva molte domande utili, ma sfortunatamente non riesce a presentare una soluzione al suo problema.1 Conclude suggerendo che una soluzione potrebbe non essere possibile.Sfortunatamente, il suo articolo è stato letto da molti come "prova" di quella speculazione.Da quando è stato pubblicato ci sono stati molti esempi di componenti generici di eccezione, tra cui i contenitori di libreria standard C ++.

E in effetti posso pensare a modi per rendere sicure le eccezioni delle classi modello.A meno che tu non abbia il controllo su tutte le sottoclassi, potresti comunque avere un problema.Per fare ciò è possibile creare typedef nelle classi che definiscono le eccezioni lanciate da varie classi template.Penso che il problema sia affrontarlo sempre in un secondo momento invece di progettarlo dall'inizio, e penso che sia questo sovraccarico il vero ostacolo.

Specifiche delle eccezioni = spazzatura, chiedi a qualsiasi sviluppatore Java di età superiore ai 30 anni

Autorizzato sotto: CC-BY-SA insieme a attribuzione
Non affiliato a StackOverflow
scroll top