Exception Handling bei abgeleiteten Klassen von exception



  • Hallo,

    ich wollte grade testen, wie das mit dem Abfang von Ausnahmen bei abgeleiten Klassen funktioniert.
    Ich habe dazu ein kleines Programm geschrieben was im Grunde nur eine wirft und gleich wieder fangen soll.

    Beispielweise

    #include<iostream>
    #include<stdexcept>
    #include<conio.h>
    using namespace std;
    
    int main()
    {
    try
      {
      throw runtime_error();
      }      
    catch(exception& test)
      {
      test.what();     
      }
    getch();
    return 0;
    }
    

    Soweit so gut dacht ich mir, theoretisch müsste mir das Programm den Namen der Exceptionklasse zurückliefern. Dachte ich... jedenfalls hats mit "bad_exception" funktioniert, aber wenn ich es mit "runtime_error", "logic_error" und abgeleiteten Fehlerklassen versuche kommt im Grunde folgende Meldung von meinem Compiler:

    E:\code\raw\std_exc_test2.cpp
    In function `int main()': no matching function for call to `std::runtime_error::runtime_error()'

    e:\programme\Dev-Cpp\include\c++\3.4.2\stdexcept:109 candidates are: std::runtime_error::runtime_error(const std::runtime_error&)
    std::runtime_error::runtime_error(const std::string&)

    Jetzt weiss ich a) nicht wirklich was mit der Fehlermeldung anzufangen und b) nicht weiss warum es bei diesen klassen nicht Funktioniert und bei "bad_exception" schon.

    Jemand ne Idee dazu?

    Dann das nächste:
    Ich habe eine eigene Fehlerklasse geschrieben und diese von "exception" erben lassen. Das Programm ist das selbe wie oben, nur dass eben eine Ausnahme meiner Fehlerklasse geworfen wird.
    Ansich dacht ich das müsste so funktionieren, der compiler quengelt nicht, aber scheinbar wird die Ausnahme nicht gefangen, denn trotz getch(), wird das programm beendet sobald ichs gestartet habe.



  • Nunja, schau Dir doch einfach mal die Konstruktoren von runtime_error und Konsorten an. Dein Compiler sagt Dir lediglich, dass es keinen davon gibt, der ohne Parameter auskommt.

    BTW: Du gibst what() nicht aus, also kann auch nix auf dem Bildschirm erscheinen.

    Zu dem zweiten Problem: Wenn Du von exception erbst, sollte es eigentlich gehen. Wir benötigen Code 😉

    BTW2: Exceptions sollte man als konstante Referenz fangen.


  • Mod

    LordJaxom schrieb:

    BTW2: Exceptions sollte man als konstante Referenz fangen.

    warum?



  • Ich weiss, normalerweise fang ich auch mit Referenzen, das mit der Konstante ist optional 😉

    Das Problem ist dass der Compiler meckert wenn ich sage

    catch(exception& &test)
    

    cannot declare reference to `class std::exception&'

    Das mit dem cout war nen Flüchtigkeitsfehler von mir eigentlich ist das da 😉

    Code kannst du gerne haben

    #include<iostream>
    #include<stdexcept>
    #include<conio.h>
    using namespace std;
    
    class eC_Beispiel : exception{};
    
    int main()
    {
    try
      {
      throw eC_Beispiel();
      }      
    catch(exception& &test)
      {
      cout << test.what();     
      }
    getch();
    return 0;
    }
    


  • @camper:
    Da wage ich Dir ja kaum drauf zu antworten. Fast jedem anderen, aber Dir ;). Nunja, so habe ich es gelernt, jedoch lass ich mich gerne eines besseren belehren (wenn's plausibel ist):

    Um auszudrücken, dass man das Objekt innerhalb des catch-Blocks nicht zu verändern gedenkt/wünscht. Ausserdem kann der Compiler potentiell Optimierungen anwenden, die er bei einem änderbaren Objekt nicht anwenden könnte. Zwingend ist diese Art des Fangens jedoch nicht (deshalb ja auch sollte und nicht muss).



  • Shilka schrieb:

    cannot declare reference to `class std::exception&'

    Auch ein Flüchtigkeitsfehler. Eine Referenz auf eine Referenz zu bilden, wird in der Tat schwierig. Schau nochmal auf die Anzahl der & im catch-Ausdruck 😉



  • Ist es eigentlich egal ob ich von der Klasse die Referenz bilde oder vom Objekt?

    Ich habe bei einigen Tutorials udn ausarbeitungen gesehn dass man das &-Zeichen hinter die Klasse schreibt und bei anderen wieder, vor den Objektbezeichner. Ehrlichgesagt verwirrt mich dass ein wenig



  • Wo Du das & hinschreibst ist völlig egal, es hat immer dieselbe Bedeutung und bezieht sich in diesem Fall auf den Typ, also auf den links davon stehenden Ausdruck.



  • Mit

    ...
    catch (exception &test){
    cout << test.what();}
    ...
    

    wurde mir im falle von einem geworfenen "bad_exception" die klassenbezeichnung "exception" ausgeworfen, als ich dann

    ...
    catch (exception& test){
    cout << test.what();}
    ...
    

    gemacht habe, ist ind er Ausgabe "bad_exception" erschienen.

    
    Desweiteren hatte ich die theorie, dass ich durch abfrage der klasse exception in catch einfach alle standard exceptions die von der klasse exception abstammen, abfangen könnte, stimmt das trotzdem? Liegt dass dann nur daran, dass es im Falle eines entsprechenden Fehlers mit der RICHTIGEN Parameterzahl geworfen wird, udn ich es mit der FALSCHEN Parameterzahl geworfen habe?


  • Nunja, das eine hat mit dem anderen so viel nicht zu tun. Du fängst eine Referenz auf ein Objekt. Wie dieses Objekt zu erzeugen ist (beim werfen) hängt einzig und allein davon ab, wie man es erzeugen kann. Wenn Du einen nicht vorhandenen Konstruktor aufrufst, wird Dir das schon der Compiler sagen. Beim Fangen hat die Anzahl der Parameter des Konstruktors keinerlei Relevanz.

    Beispiel:

    struct TestEx : public runtime_error
    {
        TestEx( string name, string wert ): // zwei Parameter
            runtime_error( name + " = " + wert ) // runtime_error hat nur einen
        {}
    };
    
    int main()
    {
        try {
            //throw exception();
            throw runtime_error( "ein parameter" );
            //throw TestEx( "farbe", "grün" );
            // wahlweise einen dieser drei entkommentieren
        } catch ( exception const& ex ) {
            cout << ex.what() << endl;
        }
    }
    

    (Ohne Rücksicht auf Namespaces und Header 😉 )



  • #include<iostream>
    #include<stdexcept>
    #include<conio.h>
    using namespace std;
    
    class eC_Beispiel : exception{};
    
    int main()
    {
    try
      {
      throw eC_Beispiel();
      }      
    catch(exception& test)
      {
      cout << test.what();     
      }
    catch(...)
      {
      cout << "hier wird gefangen :(";        
      }          
    getch();
    return 0;
    }
    

    und nochmal, also ich hab jetzt die Bestätigung. Die Ausnahme wird nicht von der Zeile

    catch(exception& test)

    gefangen, keine Ahnung warum, ist mir total schleierhaft. Obwohl "exception" ja die Basisklasse ist.



  • LordJaxom schrieb:

    Nunja, das eine hat mit dem anderen so viel nicht zu tun. Du fängst eine Referenz auf ein Objekt. Wie dieses Objekt zu erzeugen ist (beim werfen) hängt einzig und allein davon ab, wie man es erzeugen kann. Wenn Du einen nicht vorhandenen Konstruktor aufrufst, wird Dir das schon der Compiler sagen. Beim Fangen hat die Anzahl der Parameter des Konstruktors keinerlei Relevanz.

    Ah gut ich denke ich habs verstanden, what() liefert nur dann den Klassennamen zurück, wenn kein Wert hinterlegt ist ^^



  • Leite die Klasse mal public von exception ab.



  • LordJaxom schrieb:

    Leite die Klasse mal public von exception ab.

    *groschen runterfall*

    OMFG

    Danke, dass ist wirklich nen Blödheitsfehler, eigentlich hab ichs die ganze Zeit vorher genau so gemacht, nur beim testen grad verrafft ^^°°°°°°°°°°°°°°°

    🤡



  • Noch eine Frage die mir grade eingefallen ist:

    Es ist durchaus sinnvoll, auch eigene Ausnahmen von exception abzuleiten. Dazu muss die Funktion what() implementiert werden, da diese virtuell ist.

    Das stand in einer Tutorialquelle, und bezog sich darauf, what() komplett neu in die abgeleite Klasse aufzunehmen. Stimmt das? ich meine bei mir funktioniert dass einfach so über die Vererbung ohne für mich erkkenbare Probleme.



  • Wenn Du z.B. von runtime_error ableitest, hast Du bereits eine geeignete Implementierung von what() geerbt. Nämlich die, die den String den Du im Konstruktor übergibst zurückgibt.

    Die Klasse exception hat allerdings nur eine virtuelle Methode what(), die einen implementation-defined String zurückgibt. Wenn Du eine sinnvolle Nachricht darüber empfangen willst, musst Du what() überschreiben und entsprechend implementieren.

    Ich persönlich halte es jedoch für sinnvoll, statt von exception direkt von runtime_error oder logic_error abzuleiten, aus oben genanntem Grund. runtime_error, wenn es sich wirklich um eine Exception handelt, die zur Laufzeit auftreten _kann_, aber nicht _sollte_, und logic_error, wenn es sich um eine nicht abgefangene Fehlbenutzung (also eigentlich Programmierfehler) handelt.



  • Seh ich das richtig:

    Wenn ich über exception fange, bekomme ich per what nur grob um was es sich dreht und wenn ich runtime_error abfrage genaueres mitgeteilt?

    Im Fall einer leeren Fehlerklasse wird dann der Name selbiger zurück geliefert, im Fall einer erweiterten/gefüllten Fehlerklasse bekomm ich nur das zurück, was ich per Parameter beim Wurf übermittelt hab?



  • Anders:
    Du bekommst von what() das zurück, was die Implementierung von what() Dir zurückgibt. Im Falle von runtime_error ist das der String, den Du dem Konstruktor übergibst (also im Grunde was immer Du willst wenn Du selbst ableitest), im Falle von exception ist das implementation-defined.

    In diesem Falle ist das wohl eine Typinformation, also der Klassenname. Implementation-defined heisst aber dass Du Dich darauf nicht verlassen kannst, d.h. jeder andere Compiler (bzw. Standardbibliothek) kann das anders handhaben.

    EDIT:
    Das hängt allerdings nicht davon ab was du fängst, sondern davon was Du wirfst. Deshalb ist what() ja virtuell.



  • mit andern worten: wenn ich what() nicht selbst in meine klasse implementiert habe ist es vollkommen egal was ich mache, wenn mein fehlerobjekt dann what() aufruft, gibt er mir zurück wies in der klasse "exception" definiert wurde, mit andern worten, es erscheint u.a. der klassenname.



  • Wenn Du von runtime_error (bzw. logic_error) ableitest bekommst Du den String zurück, auch wenn Du eine exception fängst. Weil what() virtuell ist. Wenn Du von exception direkt ableitest: ja 😉


Anmelden zum Antworten