2009-07-21 11 views
17

Dopo aver attraversato alcuni link su gestione delle eccezioni (1, 2, e 3), so che i programmi C++ possono gettare praticamente nulla come eccezioni (int, char*, string, exception classe). So che std::exception è la classe base per le eccezioni standard lanciate dal programma. Comunque, sto cercando di progettare un try ... catch blocco come tale:Che tipo di eccezione dovrei lanciare?

try 
{ 
    MyFunc(); 
} 
catch (certain exceptions) 
{ 
    // deal with the exception accordingly 
} 
catch (the rest of the exceptions) 
{ 
    // deal with these accordingly 
} 

mentre MyFunc() contiene i seguenti:

void MyFunc() 
{ 
    ... 
    if (certain condition is true) throw exception; 
    ... 
} 

Il guaio è che in quella parte del MyFunc funzione, mi Non sono sicuro di quale tipo di eccezione dovrei lanciare. Per mantenere il codice pulito implementando la mia classe di eccezioni, non ho idea di quale sarebbe un buon modo per implementare tale classe di eccezioni.

risposta

18

Si otterrebbe la propria classe da std::exception, in modo che vi sia un modo per gestire in modo uniforme le eccezioni.

Se questo sembra eccessivo, è possibile lanciare std::logic_error o uno degli altri tipi di eccezione standard destinati alle applicazioni da utilizzare.

È inoltre possibile utilizzare questi come classi di base per le proprie eccezioni più specifiche: ciò consente di risparmiare un po 'di lavoro perché si occupano di implementare il metodo what per l'utente.

Si noti che le gerarchie di eccezioni profonde possono essere inutilizzabili, perché in pratica si sta facendo un'ipotesi su come classificare gli errori, ei vostri clienti potrebbero non essere d'accordo.

1

Se è possibile utilizzare boost, è necessario farlo. Fare riferimento a this link su come usare le eccezioni di boost. È inoltre possibile progettare la propria gerarchia di classi di eccezioni come indicato da altre risposte, ma è necessario occuparsi di aspetti sottili come i requisiti di "non-ordine" dal metodo "cosa". Un disegno di base su come questo può essere fatto sulle linee di boost :: eccezione è spiegato qui di seguito: -

#include <string> 
#include <memory> 
#include <stdexcept> 

/************************************************************************/ 
/* The exception hierarchy is devised into 2 basic layers. 
    System exceptions and Logic exceptions. But Logic exceptions are 
    convertible to the ultimate base 'System' in the system layer. 
*************************************************************************/ 

// the system exception layer 
    namespace ExH 
    { 
    namespace System { 
     // This is the only way to make predefined exceptions like 
     // std::bad_alloc, etc to appear in the right place of the hierarchy. 
     typedef std::exception Exception; 
     // we extend the base exception class for polymorphic throw 
     class BaseException : public Exception { 
     public: 
     BaseException() throw() {} 
     explicit BaseException(char const* /*desc*/) throw() 
      : Exception() 
     {} 
     BaseException(BaseException const& that) 
      : Exception(that) 
     {} 
     virtual void raise() const { throw *this; } // used to throw polymorphically 
     virtual ~BaseException() throw() {} 
     }; 
     // module level classes compose and catch the descriptive 
     // versions of layer-exceptions 
     class DescriptiveException : public BaseException { 
     public: 
     explicit DescriptiveException (char const* description) throw() 
      : description_(description) 
     { } 
     explicit DescriptiveException (std::string const& description) throw() 
      : description_(description.c_str()) 
     { } 

     virtual ~DescriptiveException() throw() {} 

     DescriptiveException (DescriptiveException const& src) throw() 
      : BaseException(src) 
     { 
      this->description_ = src.description_; 
     } 
     DescriptiveException& operator= (DescriptiveException const& src) throw() 
     { 
      if (this != &src) 
      { 
       this->description_ = src.description_; 
      } 
      return *this; 
     } 

     /*virtual*/ char const* what() const throw() { return description_; } 
     /*virtual*/ void raise() const // used to throw polymorphically 
     { throw *this; } 
     protected: 
     DescriptiveException() throw(); 
     private: 
     char const* description_; 
     }; 

    } 
    } 

// the logic exception layer 
    namespace ExH 
    { 
    namespace Logic 
    { 

     // Logic::Exception inherits from System::Exception for the 
     // following reason. Semantically for some part of the 
     // system particular instance of Logic::Exception may seem as 
     // opaque System::Exception and the only way to handle it would 
     // be to propagate it further. In other words Logic::Exception 
     // can be seamlessly "converted" to System::Exception if there is 
     // no part of the system interested in handling it. 
     // 
     class BaseException : public System::BaseException 
     { 
     public: 
     BaseException() throw() {} 
     explicit BaseException(char const* desc) throw() 
      : System::BaseException(desc) 
     {} 
     BaseException(BaseException const& that) 
      : System::BaseException(that) 
     {} 
     virtual void raise() const { throw *this; } // used to throw polymorphically 
     virtual ~BaseException() throw() {} 
     }; 
     // module level classes compose and catch the descriptive 
     // versions of layer-exceptions 
     class DescriptiveException : public BaseException { 
     public: 
     explicit 
     DescriptiveException (char const* description) throw() 
      : description_(new std::string(description)) 
     { } 
     explicit 
     DescriptiveException (std::string const& description) throw() 
      : description_(new std::string(description)) 
     { } 
     DescriptiveException(DescriptiveException const& src) throw() 
      : BaseException(src) 
     { 
      // copy the string 
      std::string* str = new std::string(src.description_.get()->c_str()); 
      description_.reset(str); 
     } 

     virtual ~DescriptiveException() throw() {} 
     /*virtual*/ char const* what() const throw() { return description_->c_str(); } 
     /*virtual*/ void raise() const { throw *this; } 
     private: 
     DescriptiveException& operator= (DescriptiveException const& src) throw(); // copy disabled 
     std::auto_ptr<std::string> description_; // do not use std::string, as it can throw 
     }; 
    } 
    } 


/************************************************************************/ 
/* Users of the exception hierarchy compose specific exceptions as and 
when needed. But they can always be caught at the System::Exception base 
class level. Some of the standard conversion examples are demonstrated :- 

class MyClass { 
public: 
    class Exception_ {}; 
    typedef 
    Compound <Exception_, Logic::DescriptiveException> 
    Exception; 

    class InvalidArgument_ {}; 
    typedef 
    Compound <InvalidArgument_, Exception> 
    InvalidArgument; 

    class NotInitialized_ {}; 
    typedef 
    Compound <NotInitialized_, Exception> 
    NotInitialized; 
public: 
    void myFunction1() const throw(NotInitialized); 
    void myFunctionN() const throw(NotInitialized); 
}; 

void MyClass::myFunction1() const { 
    throw NotInitialized("Not Inited!"); 
} 

void MyClass::myFunctionN() const { 
    try { 
    // call myFunction1() 
    } 
    catch(NotInitialized const& e){ 
    // use e 
    } 
} 

This has to be per-class basis. The exposed module will have an exception 
specification which will catch all the sub-class exceptions. The calling 
module will in turn rely on this exception-specification. This will allow 
us to have generalized exception-catching at the application-level and 
more specialized exception-catching at the specific module level.  */ 
/************************************************************************/ 

// a simple template to compose the exceptions as per conversion requirements 
    namespace ExH 
    { 
    template <typename Type, typename Base> 
    class Compound : public Base 
    { 
    public: 
     explicit Compound (char const* description) throw() 
     : Base(description) 
     {} 
     explicit Compound (std::string const& description) throw() 
     : Base(description) 
     {} 

     Compound (Compound const& src) throw() 
     : Base(src) 
     {} 

     virtual ~Compound() throw() {} 
    protected: 
     Compound() throw() {} 
    private: 
     Compound& operator= (Compound const& src) throw(); // disable copy 
    }; 

    } 
+0

Sarebbe usare il boost essere una buona idea, anche se tutta la mia isn progetto state usando le librerie di boost ancora? – stanigator

+0

Probabilmente no. La gerarchia di cui sopra può essere invece utilizzata e modificata in base alle proprie esigenze. Ho avuto un successo moderato usando livelli di eexception di questo tipo in progetti di grandi dimensioni. Inoltre, potrebbero verificarsi problemi di portabilità minori se si utilizza la compilazione incrociata utilizzando boost in quanto fortemente templato. Alcuni compilatori potrebbero non essere conformi allo standard C++. – Abhay

4

ho pensato che potrebbe essere interessante per pubblicare un codice vero e proprio per un cambiamento. Questa è la classe di eccezione la mia libreria utilità utilizza:

//--------------------------------------------------------------------------- 
// a_except.h 
// 
// alib exception handling stuff 
// 
// Copyright (C) 2008 Neil Butterworth 
//--------------------------------------------------------------------------- 

#ifndef INC_A_EXCEPT_H 
#define INC_A_EXCEPT_H 

#include "a_base.h" 
#include <exception> 
#include <sstream> 

namespace ALib { 

//------------------------------------------------------------------------ 
// The only exception thrown directly by alib 
//------------------------------------------------------------------------ 

class Exception : public std::exception { 

    public: 

     Exception(const std::string & msg = ""); 
     Exception(const std::string & msg, int line, 
         const std::string & file); 

     ~Exception() throw(); 

     const char *what() const throw(); 
     const std::string & Msg() const; 

     int Line() const; 
     const std::string & File() const; 

    private: 

     std::string mMsg, mFile; 
     int mLine; 
}; 

//------------------------------------------------------------------------ 
// Macro to throw an alib exception with message formatting. 
// Remember macro is not in ALib namespace! 
//------------------------------------------------------------------------ 

#define ATHROW(msg)            \ 
{                 \ 
    std::ostringstream os;           \ 
    os << msg;              \ 
    throw ALib::Exception(os.str(), __LINE__, __FILE__);   \ 
}                 \ 


} // namespace 

#endif 

E questo è il file cpp:

//--------------------------------------------------------------------------- 
// a_except.h 
// 
// alib exception handling stuff 
// 
// Copyright (C) 2008 Neil Butterworth 
//--------------------------------------------------------------------------- 

#include "a_except.h" 
using std::string; 

namespace ALib { 

//--------------------------------------------------------------------------- 
// exception with optional message, filename & line number 
//------------------------------------------------------------------------ 

Exception :: Exception(const string & msg) 
    : mMsg(msg), mFile(""), mLine(0) { 
} 

Exception :: Exception(const string & msg, int line, const string & file) 
    : mMsg(msg), mFile(file), mLine(line) { 
} 

//--------------------------------------------------------------------------- 
// Do nothing 
//--------------------------------------------------------------------------- 

Exception :: ~Exception() throw() { 
} 

//------------------------------------------------------------------------ 
// message as C string via standard what() function 
//------------------------------------------------------------------------ 

const char * Exception :: what() const throw() { 
    return mMsg.c_str(); 
} 

//------------------------------------------------------------------------ 
// as above, but as C++ string 
//------------------------------------------------------------------------ 

const string & Exception :: Msg() const { 
    return mMsg; 
} 

//--------------------------------------------------------------------------- 
// File name & line number 
//--------------------------------------------------------------------------- 

int Exception :: Line() const { 
    return mLine; 
} 

const string & Exception :: File() const { 
    return mFile; 
} 

} // namespace 

// end 
+0

Il tuo primo Exception ctor dovrebbe essere 'explicit' e' ATHROW (x) 'sarebbe meglio definito per' do {std :: stringstream _s; _s << (x); lanciare ALib :: Exception (_s.str(), __FILE__, __LINE__)} while (0) 'per evitare problemi di precedenza degli operatori e consentire a' ATRHOW (x) 'di usare come un'istruzione C/C++ corretta. –

+0

Prendo il tuo punto di vista esplicito. Chiamare lo stringstream _s sarebbe una cattiva idea, dato che il nome è riservato nello scope namespace. E a differenza di altri, non ho mai trovato il modo di costruire macro che valga la pena, perché non ho mai avuto problemi senza, probabilmente a causa delle mie altre pratiche di programmazione. –

+2

Se provenissi da std :: runtime_error alcuni di questi sarebbero già stati gestiti per te! –

12

Ecco un frammento di codice che mostra come estendere e utilizzare lo std :: eccezione classe: (a proposito, questo codice ha un bug, che spiegherò più avanti).

#include <iostream> 
#include <string> 
#include <exception> 

class my_exception : public std::exception 
{ 
public: 
    explicit my_exception(const std::string& msg) 
     : msg_(msg) 
    {} 

    virtual ~my_exception() throw() {} 

    virtual const char* what() const throw() 
    { 
     return msg_.c_str(); 
    } 

private: 
    std::string msg_; 
}; 

void my_func() throw (my_exception&) 
{ 
    throw my_exception("aaarrrgggg..."); 
} 

int 
main() 
{ 
    try 
    { 
     my_func(); 
    } 
    catch (my_exception& ex) 
    { 
     std::cout << ex.what() << '\n'; 
    } 
    return 0; 
} 

Nota che il costruttore è esplicita e il distruttore e ciò che() vengono dichiarate (con throw()) per indicare che essi stessi non generare eccezioni. Questo è dove si trova l'errore. È garantito che la chiamata a msg_.c_str() non genererà eccezioni proprie? Che dire del costruttore di stringhe che stiamo usando per inizializzare msg_? Può anche aumentare le eccezioni. Come possiamo progettare una classe di eccezioni che sia al sicuro dalle eccezioni generate dagli oggetti membri? La risposta è - eredita da std :: runtime_error o una sottoclasse simile std :: exception.Quindi il modo corretto per implementare my_exception sarebbe:

class my_exception : public std::runtime_error 
{ 
public: 
    my_exception(const std::string& msg) 
     : std::runtime_error(msg) 
    { } 
}; 

Non dobbiamo ignorare quello che(), in quanto è già implementato in std :: runtime_error. La corretta gestione del buffer dei messaggi viene eseguita da std :: runtime_error, quindi possiamo essere sicuri che la mia eccezione non genererà alcun errore sconosciuto in fase di esecuzione.

0

Di solito è necessario ricavare le proprie classi di eccezioni da std :: exception e le relative derivate per rappresentare errori rilevanti per il proprio dominio dell'applicazione, ad esempio se si gestiscono i file si deve avere FileNotFoundException che include il percorso file e altre informazioni rilevanti, questo In questo modo puoi creare blocchi catch per gestire determinati tipi di errore e tralasciare altri, dovresti anche evitare di lanciare o catturare eccezioni non di classe.

Dai un'occhiata alla simili gerarchie di eccezione in .NET e Java per vedere come modellare errori generali (file errori, IO errori, errori di rete, ecc)

Problemi correlati