Domanda

Ho sentito che non dovrebbe mai buttare una stringa perché c'è una mancanza di informazioni e ti cattura eccezioni non ci si aspetta per la cattura. Quali sono le buone pratiche per generare eccezioni? Non si eredita una classe di eccezioni di base? Hai molte eccezioni o pochi? fai MyExceptionClass & o const MyExceptionClass &? ecc Inoltre so che le eccezioni non dovrebbero mai stati gettati in distruttori

Io aggiungo che ho capito di progettazione per contratto e quando buttare eccezione. Mi sto chiedendo come devo generare eccezioni.

È stato utile?

Soluzione

A mio parere, una funzione dovrebbe generare un'eccezione se non riesce a mantenere la sua "promessa", se deve rompere il suo "contratto". La firma della funzione (nome e parametri) determinare il suo contratto.

Date queste due funzioni membro:

const Apple* FindApple(const wchar_t* name) const;
const Apple& GetApple(const wchar_t* name) const;

I nomi di queste funzioni così come i loro valori di ritorno indicano per me che, nel caso di FindApple la funzione è perfettamente in grado di restituire NULL quando non è stata trovata la mela corretta, ma nel caso di GetApple si sta aspettando una mela per tornare. Se questa seconda funzione non può mantenere la sua promessa, deve generare un'eccezione.

Le eccezioni sono pensati per quelle condizioni eccezionali in cui una funzione non ha altro modo di riportare queste condizioni. Se si decide di fare una parte della promessa (leggi: firma della funzione). Allora si può riferire che condizione senza un'eccezione

Si noti che, nel caso di FindApple , tocca al chiamante per decidere come gestire la condizione di "non trovare la mela giusta", perché non è più una condizione eccezionale.

Si potrebbe essere tentati di cercare di evitare tutte le eccezioni, ma che significa che si deve tenere conto di tutte le possibili condizioni eccezionali, e si sta posizionando l'onere per il chiamante, invece. Il chiamante deve verificare la presenza di "condizioni di errore", quindi.

In definitiva, un'eccezione deve essere gestita, ma solo dal chiamante che sa come gestire una particolare condizione in modo utile . E voglio dire questo nella più ampia interpretazione possibile: un servizio che dà up sarà di riprovare più tardi, un'interfaccia utente che fornisce un messaggio di errore utile, una web app che presenta uno schermo "oops", ma che recupera bene, ... e così via .

Dave

Altri suggerimenti

Una cosa fondamentale è quello di prenotare eccezioni solo per situazioni eccezionali. Non utilizzarli per il controllo di flusso. Per esempio, "File non trovato" non dovrebbe essere un'eccezione, dovrebbe essere un codice di errore o il valore di ritorno (a meno che il file è qualcosa che deve esiste, ad esempio un file di configurazione). Ma se un file scompare improvvisamente, mentre si sta elaborarlo, quindi un'eccezione è una buona scelta.

Quando le eccezioni sono usati con parsimonia, non è necessario per trasformare il vostro codice in un try-catch -spaghetti al fine di evitare di ricevere eccezioni incomprensibili-in-the-contesto dagli strati più profondi.

Con le eccezioni standard! Se si dispone di un errore specifico, cercare di evitare con valore di ritorno. Se sono per utilizzare le eccezioni, definire l'eccezione personalizzata che eredita da Exception e creare un messaggio personalizzato.

A volte può capitare che non si è in grado di restituire il codice di errore ad es. quando hai bisogno di contesto esatto di quando situazione di errore si è verificato, ad esempio. quando è necessario per propagare lo stato di errore 3 livelli fino -. ti contesto sciolto

In questa classe situazione personalizzato è la soluzione migliore. Io uso questo approccio, definendo le mie classi in linea (non c'è cpp per loro, solo .h) ad esempio:.

class DeviceException {
    ;
}

class DeviceIOException: public DeviceException {
    DeviceIOException(std::string msg, int errorCode);
}

ecc.

Ho quindi posso giudicare / agire su di eccezione per natura e per le informazioni contenute all'interno.

Ho sempre un'eccezione con un messaggio di cui si è verificato e che cosa ha causato che accada:

throw NException("Foo::Bar", "Mungulator cause a stack overflow!");

È quindi possibile utilizzare queste stringhe in messageboxes ecc.

ho sempre cattura tramite

catch (NException& ex) { ... }

Se si esegue Windows si può passare il valore di errore e hanno una funzione derivare il messaggio di errore. Il miglior esempio di questo è in Windows tramite C / C ++ di Jeffrey Richter .

Lanciare puntatori non è probabilmente una buona cosa, in quanto complica la proprietà dell'oggetto lanciato. Tipo Classe eccezioni sono probabilmente meglio di fondamenti semplicemente perché possono contenere ulteriori informazioni sul motivo per l'eccezione.

In usando una gerarchia di classe o di classe ci sono un paio di punti che si dovrebbe prendere in considerazione:

  1. Sia il costruttore di copia e distruttore dell'oggetto eccezione non deve mai generare un'eccezione. Se fanno sei programma sarà terminare immediatamente. (ISO 15,5 / 1)

  2. Se i vostri oggetti eccezione hanno di base classi, quindi utilizzare l'ereditarietà pubblica.
    Un gestore sarà selezionata solo per un derivata classe di base se la base classe è accessibile . (ISO 15,3 / 3)

  3. Infine, (per tutti i tipi di eccezione) garantire che l'espressione viene gettato non può stesso risultato un'eccezione essere gettato.

Ad esempio:

class Ex {
public:
  Ex(int i) 
  : m_i (i)
  {
    if (i > 10) {
      throw "Exception value out of range";
    }
  }

  int m_i;
};


void foo (bool b) {
  if (! b) {
     // 'b' is false this is bad - throw an exception
     throw Ex(20);    // Ooops - throw's a string, not an Ex
  }
}

Si dovrebbe sempre buttare una classe di eccezione derivata da std :: eccezione. Questo permette una certa consistenza alla vostra interfaccia e permette una maggiore flessibilità ai clienti di questi metodi o funzioni. Per esempio, se si desidera aggiungere una cattura tutto handler si può essere in grado di aggiungere un blocco

catch(std::exception& e)

e da fare con esso. (Anche se spesso non sarà in grado di farla franca che se non si controlla tutto il codice che può gettare).

Io tendo a lanciare solo eccezioni previste dalla norma (vale a dire std :: runtime_error), ma se si desidera fornire granularità in più per i gestori, si dovrebbe sentire liberi di derivare il proprio da std :: eccezione. Vedere il C ++ FAQ lite .

Inoltre, si dovrebbe lanciare una temporanea e prendere per riferimento (per evitare la copia ctor essere richiamato al vostro sito cattura). Lanciare puntatori è anche visto di buon occhio in quanto non è chiaro chi dovrebbe ripulire la memoria. C ++ Domande frequenti Lite si occupa di questo.

Per un progetto in corso, abbiamo pensato le azioni appropriate che potrebbe essere presa dal circuito principale del programma. Il programma di base accetta messaggi XML, e salva le informazioni in un database (con una buona dose di elaborazione in mezzo).

  1. errori di dati che indicano qualcosa di sbagliato l'ingresso. l'azione appropriata è quella di salvare il messaggio in una directory di registro, ma non elaborarlo.
  2. errori Infrastrutture che indicano alcuni sottocomponente (come la coda di input, un database SQL, una biblioteca JNI) non funziona correttamente. Il sonno per qualche minuto quindi ricollegare.
  3. errori
  4. di configurazione che indicano una certa configurazione aspetto è impraticabile. Uscire dal programma.

Il primo elemento è un eccezione controllata, dal momento che abbiamo preso in considerazione i dati di controllo di essere parte di interfaccia di un metodo. Gli altri sono deselezionate poiché il ciclo principale non può conoscere le implementazioni di sottocomponenti, ad esempio un'implementazione può utilizzare un database SQL, oppure può semplicemente persistere i dati in memoria -. il chiamante non ha bisogno di sapere

Se si sta gettando le eccezioni da un componente di altri sviluppatori verrà utilizzato a valle, si prega di fare loro un favore e sempre derivare le proprie classi di eccezioni (se si ha realmente bisogno di loro c.f utilizzando le eccezioni standard) da std :: eccezione. Da evitare a tutti i costi abomini pronunciano come gettare interi, HRESULTS, char *, std :: string ...

Come è stato già detto li usa per le situazioni eccezionali.

Sempre fornire un modo per l'utente di evitare un'eccezione, ad es. se si dispone di metodo, che getteranno se qualcosa va storto in questo modo:

public void DoSomethingWithFile() {
    if(!File.Exists(..))
        throw new FileNotFoundException();
}

Fornire un altro metodo per l'utente di chiamare:

public bool CanDoSomething() {
    return File.Exists(..);
}

In questo modo si può evitare il chiamante eccezioni, se vuole. Non esitate a gettare se qualcosa non va -. "Fail fast", ma sempre fornire percorso senza eccezione

Anche tenere la gerarchia di classe di eccezione piatta e dare un'occhiata alle eccezioni standard come InvalidStateException e ArgumentNullExcpetion.

Ecco un semplice esempio di un'eccezione che prende a malapena le risorse:

class DivisionError {};
class Division
{
public:
    float Divide(float x, float y) throw(DivisionError)
    {
        float result = 0;
        if(y != 0)
            result = x/y;
        else
            throw DivisionError();
        return result;
    }
};

int main()
{
    Division d;
    try
    {
        d.Divide(10,0);
    }
    catch(DivisionError)
    {
        /*...error handling...*/
    }
}

La classe vuota che viene generata non si assume alcuna risorsa o molto pochi ...

Dalla FAQ C ++, [17,12] Cosa devo buttare ? :

  

In generale, è meglio lanciare oggetti, non built-in.   Se possibile, si dovrebbe buttare le istanze di classi che   derivare (in ultima analisi) dalla classe std::exception.

... e

  

La pratica più comune è quello di lanciare una temporanea:    (Vedere l'esempio che segue)

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