2009-07-21 19 views
17

Nach der Handhabung durch einige Links auf Ausnahme gehen (1, 2 und 3), ich weiß, dass C++ Programme ziemlich alles, was als Ausnahmen auslösen können (int, char*, string, exception Klasse). Ich weiß, dass std::exception die Basisklasse für Standardausnahmen ist, die vom Programm ausgelöst werden.Welche Art von Ausnahme sollte ich werfen?

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

während MyFunc() enthält folgende Komponenten:: Aber ich bin ein try ... catch Block als solche zu entwerfen versucht

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

Das Problem ist, dass in diesem Teil MyFunc Funktion I bin mir nicht sicher, welche Art von Ausnahme ich werfen sollte. Um den Code sauber zu halten, indem ich meine eigene Exception-Klasse implementiere, habe ich keine Ahnung, was eine gute Möglichkeit wäre, eine solche Exception-Klasse zu implementieren.

Antwort

18

Sie würden Ihre eigene Klasse von std::exception ableiten, so dass es eine Möglichkeit gibt, Ausnahmen gleichmäßig zu behandeln.

Wenn dies wie Overkill aussieht, können Sie std::logic_error oder einen der anderen Standardausnahmetypen, die für Anwendungen bestimmt sind, verwenden.

Sie können diese auch als Basisklassen für Ihre eigenen spezifischen Ausnahmen verwenden: Dies spart ein wenig Arbeit, weil sie sich um die Implementierung der what Methode für Sie kümmern.

Beachten Sie, dass tiefe Ausnahmehierarchien unbrauchbar sein können, da Sie im Voraus eine Schätzung über die Kategorisierung der Fehler durchführen und Ihre Kunden möglicherweise nicht zustimmen.

1

Wenn Sie Boost verwenden können, sollten Sie dies tun. Informationen zum Verwenden von Boost-Ausnahmen finden Sie unter this link. Sie können auch Ihre eigene Exception-Klassenhierarchie entwerfen, wie in anderen Antworten angegeben, aber Sie müssen sich um subtile Aspekte wie "Notch-Anforderungen" von der What-Methode kümmern. Ein grundlegender Entwurf auf, wie sich dies auf den Linien von boost :: exception erfolgen wird im Folgenden erläutert: -

#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

Would mit boost eine gute Idee, auch wenn meine gesamte Projekt isn sein Verwenden Sie noch keine Boost-Bibliotheken? – stanigator

+0

Wahrscheinlich nicht. Die obige Hierarchie kann stattdessen verwendet und entsprechend Ihren Bedürfnissen modifiziert werden. Ich habe mäßigen Erfolg mit so genannten eexception-level in großen Projekten. Außerdem kann es zu geringfügigen Portabilitätsproblemen kommen, wenn Sie die Cross-Compilierung mit Boost verwenden, da sie stark templated ist. Einige Compiler entsprechen möglicherweise nicht dem C++ - Standard. – Abhay

4

Ich dachte, es interessant sein könnte, einige echte Code für einen Wechsel zu posten. Dies ist die Exception-Klasse meine eigene Utility-Bibliothek verwendet:

//--------------------------------------------------------------------------- 
// 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 

Und das ist die CPP-Datei:

//--------------------------------------------------------------------------- 
// 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

Ihre erste Exception ctor sollte 'explicit' sein und' ATHROW (x) 'wäre besser zu' do {std :: stringstream _s; _s << (x); Wirf ALib :: Exception (_s.str(), __FILE__, __LINE__)} while (0) ', um Operator-Vorrang-Probleme zu vermeiden und' ATRHOW (x) 'als richtige C/C++ - Anweisung zu verwenden. –

+0

Ich nehme Ihren Standpunkt bezüglich explizit. Das Aufrufen von stringstream _s wäre eine schlechte Idee, da der Name im Namespacebereich reserviert ist. Und im Gegensatz zu anderen habe ich noch nie herausgefunden, dass Makros die Mühe wert sind, da ich nie ein Problem ohne sie erlebt habe, wahrscheinlich aufgrund meiner anderen Programmierpraktiken. –

+2

Wenn du von std :: runtime_error abgeleitet hättest, würden einige davon bereits für dich erledigt werden! –

12

Hier ist ein Code-Snippet, das zeigt, wie die std :: exception erweitern und zu verwenden Klasse: (BTW, dieser Code hat einen Bug, den ich später erklären werde).

#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; 
} 

Beachten Sie, dass der Konstruktor explizit ist und der destructor und was() deklarieren (mit throw()), um anzuzeigen, dass sie sich nicht Ausnahmen werfen. Hier ist der Fehler. Ist sichergestellt, dass der Aufruf von msg_.c_str() keine eigenen Exceptions auslöst? Was ist mit dem String-Konstruktor, den wir verwenden, um msg_ zu initialisieren? Es kann auch Ausnahmen auslösen. Wie können wir eine Ausnahmeklasse entwerfen, die sicher vor Ausnahmen ist, die von Memberobjekten ausgelöst werden? Die Antwort ist - erbt von std :: runtime_error oder einer ähnlichen std :: exception-Unterklasse.So ist die richtige Art und Weise my_exception umzusetzen wäre:

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

Wir außer Kraft setzen müssen nicht, was(), wie es bereits in std :: runtime_error umgesetzt wird. Die richtige Behandlung des Nachrichtenpuffers erfolgt durch std :: runtime_error, so dass wir sicher sein können, dass my_exception selbst zur Laufzeit keinen unbekannten Fehler auslöst.

0

Normalerweise sollten Sie Ihre eigenen Ausnahmeklassen von std :: exception und seinen Derivaten ableiten, um Fehler zu repräsentieren, die für Ihre Anwendungsdomäne relevant sind. Wenn Sie beispielsweise mit Dateien arbeiten, sollten FileNotFoundException den Dateipfad und andere relevante Informationen enthalten So können Sie Catch-Blöcke erstellen, um bestimmte Fehlertypen zu behandeln und andere zu umgehen. Sie sollten auch keine Ausnahmen von der Klasse werfen oder abfangen.

Werfen Sie einen Blick auf ähnliche Ausnahme Hierarchien in .NET und Java, um zu sehen, wie allgemeine Fehler zu modellieren (Dateien Fehler, IO-Fehler, Netzwerkfehler, usw.)

Verwandte Themen