exit und return



  • tweenki schrieb:

    ...das mit einer exception müsste ich mir erst durchlesen das ahbe ich nämlich noch nie gemacht und richtig angeschaut.

    Tu das !!!
    Kostet Dich vermutlich weniger Zeit, als auf breiter Ebene Returnwerte einzuführen und auszuwerten und bringt Dir hinterher einfachere/klarere/kürzere Programme.

    Gruß,

    Simon2.



  • ist es sehr schlimm wenn man nicht kapiert wie man exceptions initialisiert? ich kapier dabei gar nichts nur das amn das in try und catch blöcke machen muss. und dann löst sich der stack auf. kennt jemand ein tut wo das gut erklärt wird?



  • Zeig doch mal, was du bisher erreicht hast.

    Im Allgemeinen mußt du zwei Seiten betrachten:

    Werfer - erzeugt eine Exception und schmeißt sie dann den Aufrufstack hinunter. (theoretisch kannst du fast alles werfen, was du in die Hände bekommst, aber C++ bietet die std::exception-Hierarchie dafür an)

    void meine_funktion()
    {
      ...
      if(fehler)
        throw std::runtime_error("hier ist was schiefgelaufen");
      ...
    }
    

    Fänger - ruft (direkt oder indirekt) deine Funktion auf und fängt und verarbeitet alle ankommenden Exceptions (normalerweise nur die vom richtigen Typ)

    try
    {
      ...//ziemlich viel Code - irgendwo hier drin wird 'meine_funktion()' aufgerufen
    }
    catch(std::exception& ex)
    {
      cerr<<"Fehler: "<<ex.what();
    }
    catch(...)
    {
      cerr<<"unbekannter Fehler";
    

    (what() ist eine Hilfsmethode von std::exception und gibt aus, was für ein Fehler aufgetreten ist - in diesem Beispiel wird der String ausgegeben, den du beim throw hingeschrieben hast)

    normalerweise gibst du in der catch-Klausel den Typ an, den du auffangen willst (std::exeption& schließt auch dessen Kindklassen mit ein) - die Angabe ... reagiert auf alle unbekannten Fehler (s.o. - throw kann nahezu alles werfen) - hat allerdings keine Möglichkeit mehr, etwas genaueres zu erfahren als "hier kam eine Exception".



  • in c++ in 21 Tagen wird das irgendwie ganz anders erklärt. ich kapier des dort jedenfalss nicht.
    zu was zählt man alles exceptions? fehlerhaffte eingaben oder was sonst. weil fehler haftze eingaben kann man ja mit hilfe von if fragen und so wieder korrigieren.
    mein konkretes problem ist das ich einen server programmieren möchte und dann muss man schließlich fehler beachten, zb wenn ich einen socket erstelle, dann kann das auch schief laufen und dann muss ich das irgendwie mitteilen.



  • Als Exception kannst du alles werten, was den normalen Programmablauf stört - sie werden aber normalerweise eingesetzt, wenn du dort, wo du den Fehler bemerkst, nicht sinnvoll reagieren kannst.
    (wenn der Dialog nach der Eingabe selber überprüft, ob die Eingaben korrekt sind, kann er auch reagieren - und den User um Korrektur bitten. Irgendwo tief im Inneren deiner Mathe-Bibliothek kannst du bei sinnlosen Eingabedaten nur noch eine Exception werfen (oder sinnlose Ausgaben fabrizieren))



  • also sind exceptions nur in tief verschachtelten situationen wirklich sinnvoll?



  • das muß nicht unbedingt "tief" sein (btw, ab wann gilt eine Situation als "tief verschachtelt"), das reicht schon, wenn der Fehler in einer direkt aufgerufenen Funktion auffällt:

    double sqrt(double x)
    {
    /*
      die Funktion hat keine Ahnung, woher der übergebene Wert stammt - oder was sie
      mit ungültigen Werten machen sollte, also wirft sie eine Exception und übergibt damit
      dem Aufrufer die Verantwortung
    */
      if(x<0)
        throw std::invalid_argument("keine Wurzeln aus negativen Zahlen erlaubt");
      ...
    }
    
    ...
    double input;
    cin>>input;
    /*
      wenn du jetzt schon merkst, daß du mit der Eingabe nicht weiterrechnen
      könntest, kannst du noch vernünftig reagieren und den Nutzer aufmerksam machen
    */
    cout<<sqrt(input);
    ...
    


  • tweenki schrieb:

    in c++ in 21 Tagen wird das irgendwie ganz anders erklärt. ich kapier des dort jedenfalss nicht...

    Sorry, aber das liegt in der Natur der wirklich schlechten Buchreihe.

    Zum Exceptionhandling:

    Eine Exception sollte man an dem Punkt auslösen (Strichwort: throw) wo ein Fehler auftritt, der nicht direkt an dieser Stelle behandelt wird wo er eintritt.

    Auf eine Exception sollte man an der Stelle reagieren wo man sie sinnvoll behandeln kann.

    Nehmen wir einfach mal als Beispiel das du einige Mathefunktionen in einen eigenen Header ausgelagert hast, damit du sie unter verschiedenen Umgebungen benutzen kannst. Damit du nicht von der GUI abhängig bist und sie wirklich überall verwenden kannst, ist es nicht möglich bei einer fehlerhaften Übergabe (z.B. eine 0 beim Dividenten) den Anwender zu bitten die Zahl korrigiert einzugeben. Da du also in der Funktion keine Möglichkeit hast Sinnvoll auf den Fehler zu reagieren, musst du eine andere Möglichkeit anbieten wie man auf diesen Fehler reagieren kann.

    Bei den klassischen Rückgabewerten hast du das Problem das sie zum einen den Rückgabewert belegen (im Falle einer Mathematischen Funktion hätte ich gerne die Möglichkeit einige Aufrufe zu schachteln) und auch schnell vergessen werden können. Eine andere Möglichkeit wäre z.B. den Fehlercode als Referenz in den Parametern mitzuführen. Aber grade an dem Beispiel der geschachtelten Funktionen bekommt man hier ggf. das Problem das eine fehlerhafte Rückgabe andere verherende Auswirkungen in weiteren Funktionen mit sich bringt.

    Also was macht man: Man löst im Fehlerfall in der entsprechenden Funktion eine Exception aus. Dadurch wird der Programmfluß an dieser Stelle abgebrochen (Anweisungen die danach kommen werden nicht mehr beachtet, lokal erzeugte Objekte aber bereinigt [Achtung: Kein delete bei Pointern - Ich liebe Smartpointer]) und erst mit dem catch des aktiven try-Blockes weitergemacht. Wenn man den exceptions noch unterschiedliche Typen verpasst, kann man diese auch Unterschiedlich in den catchblocken behandeln.

    cu André



  • tweenki schrieb:

    ...
    zu was zählt man alles exceptions? ....

    Mach's Dir doch nicht so schwer !
    Dir ging es doch um eine konkrete Situation, in der Du sagst "Hier will ich gar nicht mehr weitermachen" ... und da wirfst Du halt eine Exception.

    Eine "allgemeingültige und vollständige" Regel gibt's sowieso nicht. Davon zeugen z.B. die ewig langen Threads zu diesem Thema hier im Forum.
    Am Besten machst Du erstmal selbst Erfahrungen mit dem Thema.

    Gruß,

    Simon2.



  • tweenki schrieb:

    kennt jemand ein tut wo das gut erklärt wird?

    Eine Exception wirft man immer dann, wenn (Design by Contract sollte man im Hinterkopf haben) man die Postcondition einer Funktion/Methode nicht einhalten kann.

    Was den konkreten Einsatz von Exceptions anbelangt. Unbedingt die Bücher von Herb Sutter anschauen, da man einige Regeln beachten muß, damit die eigenen Klassen exception safe bzw. exception neutral sind.

    Wenn eine Exception geworfen wird, beginnt das sogenannte "stack unwinding", alle Objekte auf dem Stack werden destruiert und die Ausführung "springt im Stack zurück" solange bis irgend eine Stelle im Programm die Exception abfängt. Im schlimmsten Fall wird das Programm terminiert. Leider sind Zeiger PODs und damit passiert mit ihnen exakt gar nichts, es können so Speicherlöcher entstehen. Deshalb sollte man SmartPointer o.ä. einsetzen. Was alles zu beachten ist steht in recht gut erklärt in den Büchern von Sutter.



  • Fellhuhn schrieb:

    return ist nur für Funktionen/Methoden, nicht für das eigentliche Programm.

    Der saubere Weg ein Programm zu verlassen ist es aus main zu beenden, entweder implizit oder durch das explizite return mit Code.

    exit hat den häßlichen Seiteneffekt, daß die Destruktoren von statischen Objekten nicht aufgerufen werden. Wenn man irgend welche Singletons ö.ä. hat, kann das problematisch sein.



  • ~john schrieb:

    Fellhuhn schrieb:

    return ist nur für Funktionen/Methoden, nicht für das eigentliche Programm.

    Der saubere Weg ein Programm zu verlassen ist es aus main zu beenden, entweder implizit oder durch das explizite return mit Code.

    exit hat den häßlichen Seiteneffekt, daß die Destruktoren von statischen Objekten nicht aufgerufen werden. Wenn man irgend welche Singletons ö.ä. hat, kann das problematisch sein.

    Habe ich was anderes behauptet?



  • Fellhuhn schrieb:

    ~john schrieb:

    Fellhuhn schrieb:

    return ist nur für Funktionen/Methoden, nicht für das eigentliche Programm.

    Der saubere Weg ein Programm zu verlassen ist es aus main zu beenden, entweder implizit oder durch das explizite return mit Code.

    exit hat den häßlichen Seiteneffekt, daß die Destruktoren von statischen Objekten nicht aufgerufen werden. Wenn man irgend welche Singletons ö.ä. hat, kann das problematisch sein.

    Habe ich was anderes behauptet?

    Naja, es hatte sich schon so angehört als würdest du exit für das Beenden in betracht ziehen.

    Un der Unterschied zwischen exit und return ist etwa so, als wenn man in ein Hochhaus mehrere Treppen und Türen hinter sich gebracht hat, und sich dann überlegt wie man wieder das Haus verlässt:
    return: Man geht wieder durch die Türen und Treppen zurück und verlässt damit das Hochhaus.
    exit: Man nimmt das nächstbeste Fenster.

    Ja, es kann gut gehen. Aber mit Sicherheit nicht immer ;p

    cu André



  • Eben, wenn du du mit "return" die letzte Treppe nimmst, bist du da wo du auch durchs "exit" im 20. Stock angekommen wärst. Allerdings in einem etwas unschöneren Zustand.

    exit braucht man eigentlich nicht, Exceptions können das ebenso, und dazu sauber, regeln.

    Wobei mir bei den Exceptions bei C++ immernoch das finally fehlt...



  • Fellhuhn schrieb:

    Wobei mir bei den Exceptions bei C++ immernoch das finally fehlt...

    Wobei du die meisten derartigen Situationen durch RAII lösen kannst - am Funktionanfang legst du ein Objekt an und im Destuktor führt es alle notwendigen Aufräumarbeiten auf (und der wird sowohl im Regelfall als auch beim Stack Unwinding ausgeführt).



  • Wirkt unsauber aber sicher eine bessere Möglichkeit als den CleanUp-Code mehrfach aufzuführen.



  • Fellhuhn schrieb:

    Wirkt unsauber aber sicher eine bessere Möglichkeit als den CleanUp-Code mehrfach aufzuführen.

    finally wälzt die Veranwortung wieder auf den Nutzer der Klasse und nicht auf den Designer einer Klasse ab. Die Realität zeigt, daß der Nutzer sich mit den Problemen nicht herumschlagen will. Insofern ist finally schlechter als RAII.



  • ~john schrieb:

    finally wälzt die Veranwortung wieder auf den Nutzer der Klasse und nicht auf den Designer einer Klasse ab. Die Realität zeigt, daß der Nutzer sich mit den Problemen nicht herumschlagen will. Insofern ist finally schlechter als RAII.

    Weshalb ich auch der Meinung bin C++ braucht kein finally. Das verleitet nur zu unsauberer Programmierung 😉



  • ~john schrieb:

    exit hat den häßlichen Seiteneffekt, daß die Destruktoren von statischen Objekten nicht aufgerufen werden. Wenn man irgend welche Singletons ö.ä. hat, kann das problematisch sein.

    du verwechselst das verhalten mit abort.
    exit ruft die destruktoren von statischen objekten sehr wohl auf.
    nur automatische objekte müssen nicht unbedingt alle zerstört werden.

    btw. um sicher zu gehen, dass das passiert, empfiehlt der standard das werfen einer ausnahme, die in main gefangen wird. dort kann man ja dann auch exit aufrufen, wenn man lust hat (🙄 )



  • LordJaxom schrieb:

    ~john schrieb:

    finally wälzt die Veranwortung wieder auf den Nutzer der Klasse und nicht auf den Designer einer Klasse ab. Die Realität zeigt, daß der Nutzer sich mit den Problemen nicht herumschlagen will. Insofern ist finally schlechter als RAII.

    Weshalb ich auch der Meinung bin C++ braucht kein finally. Das verleitet nur zu unsauberer Programmierung 😉

    Um nicht nochmal dasselbe zu schreiben: 100% 👍

    Gruß,

    Simon2.


Anmelden zum Antworten