2009-10-22 13 views
17

Próbuję utworzyć niestandardowe klasy wyjątków dla biblioteki C++, nad którą pracuję. Te niestandardowe wyjątki przechwytują dodatkowe informacje, takie jak plik, numer linii itp., Potrzebne do debugowania, jeśli z jakiegoś powodu podczas testowania wyjątek nie zostanie przechwycony we właściwym miejscu. Jednak większość ludzi wydaje się polecać dziedziczenie z klasy std :: exception w STL, z czym się zgadzam, ale zastanawiałem się, może lepiej byłoby użyć dziedziczenia wielokrotnego, aby odziedziczyć po każdym z derived std::exception classes (np. Std :: runtime_error) i niestandardową klasą wyjątków, jak w poniższym kodzie?Niestandardowe wyjątki w C++

Kolejna kwestia, w jaki sposób można kopiować konstruktory i operatory przypisania w klasach wyjątków? Czy powinny być wyłączone?

class Exception{ 
    public: 
     explicit Exception(const char *origin, const char *file, 
          const int line, const char *reason="", 
          const int errno=0) throw(); 

     virtual ~Exception() throw(); 

     virtual const char* PrintException(void) throw(); 

     virtual int GetErrno(void); 

    protected: 
     std::string m_origin; 
     std::string m_file; 
     int m_line; 
     std::string m_reason; 
     int m_errno; 
}; 

class RuntimeError: public virtual std::runtime_error, public Exception{ 
    public: 
       explicit RuntimeError(const char *origin, const char *file, 
            const int line, const char *reason="", 
            const int errno=0) throw(); 
     virtual ~RuntimeError() throw(); 
}; 
+0

Co zyskałbyś na tym? Dlaczego nie skorzystać z pojedynczego dziedziczenia? Dlaczego musisz dostosować * wyjątki * Wyjątek i RuntimeError? – jalf

+0

Chciałem użyć funkcjonalności z mojej niestandardowej klasy wyjątków i utrzymywać hierarchię wyjątków STL, dziedzicząc z różnych pochodnych klas std :: exception. W ten sposób złap z: spróbuj { throw RunTimeException (...); } catch (std :: runtime_error & e) { ... } catch (std :: exception & e) {...} również złapie moją niestandardową klasę. Jednak może to być strata czasu. Mogę też po prostu spróbować { throw RunTimeException (...); } catch (std :: exception & e) {...} , ale może to utrudnić identyfikację wyjątku. Nie wiem, jaka jest typowa praktyka, gdy niestandardowe klasy wyjątków? – Tom

+0

Powiedziałbym po prostu dziedziczyć ze std :: runtime_error (lub std :: exception). Jeśli chcesz konkretnego leczenia, możesz mimo wszystko mieć wiele haczyków. –

Odpowiedz

11

Należy spróbować boost::exception

Celem Boost, wyjątkiem jest do ułatwić projektowanie klasy wyjątków hierarchie i pomóc napisać obsługę wyjątków i błędów raportowania kod .

wspiera transportowania dowolnych danych do strony zatrzaskowej, która inaczej trudne ze względu na brak wymagania rzut (15.5.1) za wyjątkiem types. Dane mogą być dodawane do dowolnego obiektu wyjątku , bezpośrednio w wyrażenia throw (15.1) lub późniejszym , gdy obiekt wyjątku propaguje do góry stos wywoławczy.

Możliwość dodawania danych do wyjątku obiektów po ich zostały przekazane do rzutu jest ważne, ponieważ często niektóre informacji potrzebnych do obsługi wyjątku jest niedostępny w kontekście gdzie wykryto uszkodzenie.

doładowania Wyjątek obsługuje również N2179 stylu kopiowanie wyjątku obiektów realizowane non-natrętnie i automatycznie przez funkcję boost :: throw_exception.

+0

Nigdy wcześniej nie stosowałem wzmocnienia. Przyjrzę się temu. Dzięki za post. – Tom

+1

Dość niesamowita biblioteka. Teraz chciałbym, abym to przeczytał wcześniej! –

17

Zastanawiałem się, może lepiej byłoby użyć wielokrotne dziedziczenie dziedziczyć z każdego std :: pochodzącej klasy wyjątków

pamiętać, że jest to problem, ze względu na fakt, że wyjątki w standardowej bibliotece pochodzą praktycznie od siebie. Jeśli wprowadzisz dziedziczenie wielokrotne, otrzymasz hierarchię wyjątków diamentowych bez dziedziczenia wirtualnego i nie będzie można złapać pochodnych wyjątków przez std::exception&, ponieważ twoja pochodna klasa wyjątków zawiera dwa podobiekty std::exception, czyniąc std::exception "niejednoznaczną klasę podstawową".

konkretny przykład:

class my_exception : virtual public std::exception { 
    // ... 
}; 

class my_runtime_error : virtual public my_exception 
         , virtual public std::runtime_error { 
    // ... 
}; 

teraz my_runtime_error wyprowadza (pośrednio) z std::exception dwukrotnie, raz przez std::run_time_error i raz przez my_exception.Od dawna nie wynika z std::exception praktycznie ta

try { 
    throw my_runtime_error(/*...*/); 
} catch(const std::exception& x) { 
    // ... 
} 

nie będzie działać.

Edit:

Chyba widziałem pierwszy przykład hierarchii klas wyjątków udziałem MI w jednej z książek Stroustrup, więc doszedłem do wniosku, że, ogólnie rzecz biorąc, jest to dobry pomysł. Fakt, że wyjątki std lib nie pochodzą właściwie od siebie, uważam za porażkę.

Kiedy ostatnio zaprojektowałem hierarchię wyjątków, użyłem MI bardzo szeroko, ale nie wywnioskowałem z klas wyjątków std lib. W tej hierarchii zostały zdefiniowane abstrakcyjne klasy wyjątków, które użytkownicy mogli je przechwycić, oraz odpowiednie klasy implementacji pochodzące z tych klas abstrakcyjnych oraz z klasy bazowej implementacji, które faktycznie zostałyby wyrzucone. Aby to ułatwić, zdefiniowałem kilka szablonów, które wykonują ciężką pracę:

// something.h 
class some_class { 
private: 
    DEFINE_TAG(my_error1); // these basically define empty structs that are needed to 
    DEFINE_TAG(my_error2); // distinguish otherwise identical instances of the exception 
    DEFINE_TAG(my_error3); // templates from each other (see below) 
public: 
    typedef exc_interface<my_error1> exc_my_error1; 
    typedef exc_interface<my_error2> exc_my_error2; 
    typedef exc_interface<my_error3,my_error2> // derives from the latter 
            exc_my_error3; 

    some_class(int i); 
    // ... 
}; 

//something.cpp 
namespace { 
    typedef exc_impl<exc_my_error1> exc_impl_my_error1; 
    typedef exc_impl<exc_my_error2> exc_impl_my_error2; 
    typedef exc_impl<exc_my_error3> exc_impl_my_error3; 
    typedef exc_impl<exc_my_error1,exc_my_error2> // implements both 
            exc_impl_my_error12; 
} 
some_class::some_class(int i) 
{ 
    if(i < 0) 
    throw exc_impl_my_error3(EXC_INFO // passes '__FILE__', '__LINE__' etc. 
          , /* ... */ // more info on error 
          ); 
} 

Patrząc na to teraz, myślę, że mógłbym się że exc_impl klasa szablon pochodzą z std::exception (lub dowolną inną klasę w hierarchia wyjątków std lib, przekazywana jako opcjonalny parametr szablonu), ponieważ nigdy nie pochodzi z żadnej innej instancji exc_impl. Ale wtedy nie było to potrzebne, więc nigdy nie przyszło mi to do głowy.

+0

+1: dość wyjaśniający – fmuecke

+0

Dzięki za post sbi. Czy tworzenie klas wyjątków z dziedziczeniem wielokrotnym byłoby dobrą praktyką? Czy też lepiej jest dziedziczyć po klasie std :: exception? – Tom

+0

@ Tom: Dodałem swoje przemyślenia jako edycję. – sbi

Powiązane problemy