Konstruktor auch ohne Erfolg realisierbar?



  • Gustav Hummel schrieb:

    Die Idee mit der factory hat sich gut angehört. Wie würde diese aussehen?
    so in etwa?:
    1. Objekt erzeugen (über factoryklasse?!)
    2. Factory behandelt das ganze wie oben(also objekt erzeugen + x-mal init()), bei Erfolg gibt sie Objekt zurück, bei Misserfolg NULL.
    2. prüfe auf Objekt = NULL

    so ungefähr. das grundprinzip ist, dass die factory alle nötigen daten besorgt und sobald sie vorliegen, ein objekt erzeugt. d.h. deine klasse braucht keinen init ballast und auch kein exception handling etc. das macht alles die factory, und wenn das erzeugen erfolgreich möglich war, liefert sie nen schlankes, vollständiges objekt zurück. wenn also die daten nicht besorgt werden konnten, wurde auch zu keinem zeitpunkt versucht, ein objekt anzulegen.



  • @Kenner....:

    Was soll ich da jetzt zu sagen? 🙄 Wie gut, dass jeder seine Meinung haben darf.

    Nur mal so: Ich kann kein "nie" bei mir entdecken. 😕 Getreu nach dem Motto: "Nobody is perfect.", vergess ich die Überprüfung auch mal. Aber es ist die Ausnahme. 😃



  • Maffe001 schrieb:

    Nur mal so: Ich kann kein "nie" bei mir entdecken. 😕 Getreu nach dem Motto: "Nobody is perfect.", vergess ich die Überprüfung auch mal. Aber es ist die Ausnahme. 😃

    Aber einen guten Grund, Exceptions nicht zu benutzen brauchst du offensichtlich auch nicht. Da müsstest du nicht ständig sinnlose Überprüfungen von sinnlosen Objekten durchführen... 🙄



  • @Kenner...:

    Also wenn du rumtrollen willst, warum tust du's nicht woanders?
    Du magst wahrscheinlich keinen Rosenkohl und trotzdem werd ich nicht versuchen ihn dir aufzuzwingen. Deine Begründung, warum du ihn nicht magst, wird eher dürftig ausfallen. Gut aber das ist dein Ding.

    @alle anderen:

    Sorry, dass ich da nochmal drauf eingestiegen bin. Intolleranz find ich einfach nur....
    Damit sind dann auch meine Reaktionen auf "Kenner" beendet.



  • Ich finde die Variante besser, wo im Konstruktor kein Fehler erzeugt werden kann 🙂



  • Auch wenn ich dir nicht das Programmieren ohne Exceptions ausreden will, möchte ich hier nur mal die Gründe die für eine Exception sprechen hinstellen:

    a) Wenn man Standardkonform programmiert, wird man zwangsweise Exceptions behandeln müssen (die STL wirft Exceptions, ebenso wie new).
    b) Exceptions sind Ausnahmefälle, keine Regeln, der Rückgabeparameter einer Funktion sollte so gewählt werden das er die Regelfälle sinnvoll abdeckt.
    c) Rückgabewerte darf man nicht vergessen, Exceptions kann man nicht vergessen (Sonst knallt es, wo beim Rückgabewert das Programm einfach irgendwas - ggf. sehr verkehrtes/schlimmes - tut).
    d) Exceptions muss man nicht zwingend am Ort des Auftretens behandeln, Rückgabewerte schon.
    e) Mit Exceptionhandling und Smartpointern kann man inkonsistente Daten nach einen Konstruktoraufruf leichter behandeln [bin kein Fan von der Ungarischen Notation, aber für die kurzform hier besser: m_ap=> Autopointer/Smartpointer als Member]:

    Fall 1 mit Smartpointer&Exceptionhandling

    Klasse::Klasse()
    :   m_apObjekt1(new Objekt1()),
        m_apObjekt2(new Objekt2()),
        m_apObjekt3(new Objekt3())
    {
    }
    

    Wenn nun bei der Objektanlage irgendeines Teilobjektes eine Exception auftritt, so werden alle vorherigen gelöscht, und der Konstruktor erstellt kein Objekt (sprich: kein Objekt oder ein Konsistentes).

    Versuch das mal ohne Exceptionhandling mit wenigen Codezeilen sinnvoll nachzubauen (noch dazu wenn vielleicht noch mehr Teilobjekte dazukommen).

    cu André



  • ich bin kein fan von exceptions.

    try
    {
     // gewaltiger, ellenlanger code
    }
    catch(...){}
    
    // weiter gehts
    

    sie verleiten zu lazy coding. gedanken darüber machen, ob auch alles klappt? input überprüfen? ach egal, wenn was schief geht, wird schon irgendwo ne exception fliegen, die man ja abfangen kann.



  • Maffe001 schrieb:

    [Edit]
    ...Ich wollte die Exceptionvariante nicht kritisieren oder meine als die bessere hinstellen....

    Das war mir klar ... aber ich wollte Deine Variante kritisieren und die Exceptionvariante als die bessere hinstellen... 😉

    Was hast Du gegen exceptions ? Die sind genau für diesen Fall gemacht.

    Ach ja: D.h. Du fragst auch bei printf() immer den Returncode ab ?

    Gruß,

    Simon2.



  • thordk schrieb:

    ...sie verleiten zu lazy coding....

    Inwieweit das ? 😕
    Ich würde eher sagen: Mit exceptions kann man sich auf das Wesentliche konzentrieren ... versuch mal, ohne exceptions "exceptions safety" (= ein Kriterium einer stabilen Schnittstelle) umzusetzen ... da wirst Du Dir die Finger brechen - und letztlich eine Art exceptions (nur mit mehr Overhead) selbst programmieren.

    Gruß,

    Simon2.



  • thordk schrieb:

    sie verleiten zu lazy coding.

    Wenn du ohne Exceptions arbeitest, ist das noch schlimmer. Vor allem wird dich niemand darauf aufmerksam machen, wenn du vergessen hast, einen Fehlerwert zu überprüfen (und was dein Programm in einem fehlerhaften Zustand macht, kann dir niemand sagen) - Exceptions fliegen dir ziemlich lautstark um die Ohren, wenn du versuchst, sie zu ignorieren.



  • @simon2:

    LOL Nein ich überprüfe nicht den Return von "printf". 😃 Ich überprüf ihn immer bei Sachen, die ich selbst geschrieben habe. Den traue ich natürlich nicht so ganz. Vielleicht ist diese Einstellung wirklich etwas blauäugig.

    Hmpf, ich seh schon. Ich werd meine Einstellung zu Exceptions überdenken müssen. Allerdings bin ich immernoch der Meinung, dass man bei Objekten, die man nicht richtig initialisiert bekommt auch weiterhin mit Rückgabewerten arbeiten kann. Wenn euch jetzt ein Gegenbeispiel einfällt, dass sich nur mit Exceptions behandeln lässt, dann immer her damit. 🙂

    [Edit]Ui, ich hab grad den Beitrag von asc entdeckt. Sorry, ging in der Aufregung unter. Dann vergesst, was ich gesagt habe. Die Gründe leuchten ein. Gut ich werde nochmal darüber meditieren und mich mit Exceptions anfreunden. :D[/Edit]



  • Und wie soll ein Konstruktor etwas als Rückgabewert übergeben? Das einzige, was der zurückgeben kann, ist ein fertig konstruiertes Objekt, also bleibt als Alternative zu einer Exception nur, this in einen Zombie-Status zu versetzen. Und wenn jede Methode erst überprüfen muß, ob sie auf ein intaktes Objekt angewendet wurde, bremst du damit dein gesamtes Programm unnötig aus.



  • CStoll schrieb:

    Und wie soll ein Konstruktor etwas als Rückgabewert übergeben? Das einzige, was der zurückgeben kann, ist ein fertig konstruiertes Objekt, also bleibt als Alternative zu einer Exception nur, this in einen Zombie-Status zu versetzen. Und wenn jede Methode erst überprüfen muß, ob sie auf ein intaktes Objekt angewendet wurde, bremst du damit dein gesamtes Programm unnötig aus.

    Du würdest fstream anders realisieren?



  • delete this;
    


  • class MyClass
    {
        public:
            MyClass(int &riRes)
            {
                if (....)
                {
                    riRes = -1;
                    return;
                }
            }
    };
    

    Ist also nur ein Quasirückgabewert. Deswegen hab ich's auch weiter oben mit "" geschrieben.



  • @Bashar: Nicht unbedingt - bei den IO-Streams kann es ja auch vorkommen, daß ein bereits existierendes Objekt später ungültig wird, also ist der inaktiv-Status sowieso notwendig. Aber bei Objekten, die nach einer erfolgreichen Initialisierung (aka Erschaffung) gültig bleiben, kostet es nur Zeit und Platz mitzuschreiben, ob sie nutzbar sind.

    @Maffe: Also da ist die Exception-Variante auf jeden Fall eleganter und sicherer:

    int dummy;//damit der Ctor glücklich ist
    MyClass test(dummy);
    test.do_something();
    

    Wenn du vergisst, das Ergebnis zu kontrollieren (und kein Compiler der Welt wird dich darauf aufmerksam machen, daß dort etwas wichtiges zurückgekommen ist), arbeitest du mit einem kaputten Objekt.

    Im Gegensatz dazu:

    try
    {
      MyClass test;
      test.do_something();
    }
    catch(...)
    {...}
    

    Wenn der Ctor eine Exception geworfen hat, springt das Programm ohne Umwege zum catch-Block - also werden auch alle Anweisungen umgangen, die mit dem (jetzt unbrauchbaren) Objekt arbeiten wollten.



  • Maffe001 schrieb:

    @simon2:

    LOL Nein ich überprüfe nicht den Return von "printf". 😃 Ich überprüf ihn immer bei Sachen, die ich selbst geschrieben habe. Den traue ich natürlich nicht so ganz. ...

    Mit "trauen" (im Sinne von "falsch programmiert") hat das eigentlich nicht viel zu tun, sondern damit, dass sich zur Laufzeit (z.B. durch Usereingaben, DB-Inhalte, NetzwerkMSG, ...) Situationen ergeben können, die falsche Ergebnisse liefern. Und da spielt es eigentlich keine Rolle, ob das bei einer selbstgeschriebenen Funktion oder einem printf() passiert. was hilft Dir z.B. das Einlesen mit scanf(), wenn die vorherige Anzeige (printf()) schief gelaufen ist ?
    Oder erst recht problematisch ist ein gescheitertes sprintf() ...

    Gruß,

    Simon2.



  • @simon2:

    Doch mit "trauen" hat das manchmal auch zu tun. Bei einigen Multithreading- oder Timergeschichten unter Windows, sind schon manchmal Effekte aufgetreten, an die ich nicht im Traum gedacht habe. (Ja ich weiss, der Fehler lag sicher an meinem Konzept.) Aber gut, jetzt wird's philosophisch.

    Ansonsten geb ich dir natürlich Recht. Man muss halt versuchen alle möglichen Zustände abzudecken.

    Was mich bei Exceptions halt immer gestört hat, sind diese try...catch-Blöcke, die den Code doch recht verschandeln. Aber ich bin bekehrt. Habt ihr gut gemacht. 😃
    Ich denke mal, dass die meisten von euch nicht an jeder Stelle, wo ein Objekt erzeugt wird, solche Blöcke einbauen, sondern eher eine zentrale Stelle haben, an denen diese Kontrolle gemacht werden. Oder seh ich das falsch?



  • Maffe001 schrieb:

    ...Doch mit "trauen" hat das manchmal auch zu tun....

    Dagegen habe ich auch nichts ... eben manchmal !
    Ich wollte nur aussagen: Selbst da, wo man einem Programm traut, ist Fehlerbehandlung sinnvoll.
    Allerdings: Wenn Du einem Code nicht "traust", hilft auch Fehlerhandling nicht wirklich weiter, weil: Wer sagt Dir denn, dass der schlechte Programmierer überhaupt eine Fehlersituation erkennt und (sei es via Returncode oder exception) sauber handlet ? 😃
    Ich behaupte mal, dass die meisten Programmierfehler nicht durch Fehlerhandling des Aufrufers unschädlich gemacht werden können ... da gibt dan die aufgerufene Funktion stumpf eine 7 statt einer 5 zurück und dem Anwender werden Stromstöße durch die Hoden gejagt und kein catch(...) der Welt hilft ihm. 😉

    Maffe001 schrieb:

    ...
    Ich denke mal, dass die meisten von euch nicht an jeder Stelle, wo ein Objekt erzeugt wird, solche Blöcke einbauen, sondern eher eine zentrale Stelle haben, an denen diese Kontrolle gemacht werden. Oder seh ich das falsch?

    JETZT kommt der Punkt, wo die Sache interessant wird: Beim Fehlerhandlingkonzept ! 😃
    Da kann ich Dir nur sagen, wie ich das entscheiden: Immer dort, wo ein Programm in einer Ausnahmesituation "noch etwas machen" (= selbsttätig regieren) kann, gehört Fehlerhandling (try/catch) hin ... und sonst nirgendwo !
    Eine exception abzufangen, bloß um sie weiterzuwerfen (ggf. umgewandelt), ist IMO Unsinn. Man braucht auch GAR keinen try/catch, wenn man möchte/es nicht stört, dass das Programm einfach terminiert bei einer exception.
    Ausnahmen:
    - Manche Komponentenschnittstellen sind so definiernt, dass keine exceptions sie queren dürfen oder (aus technischen Gründen) können. Hier muss man ein catch(...) einfügen und auf die schnittstellenkonforme Fehlerhandlingtechnik mappen.
    - Destruktoren sollten niemals exceptions werfen ! Also entweder nur Funktionen aufrufen, die keine schmeißen können (z.B. andere Destruktoren, delete oder ...) oder mittels catch(...) sicherstellen.

    Das ist ja das Schöne an den exceptions: Man braucht sie nur da zu handeln, wo man auch was tun möchte und trotzdem ist garantiert, dass das Programm nicht falsch arbeitet.

    Gruß,

    Simon2.



  • Maffe001 schrieb:

    @simon2:

    Doch mit "trauen" hat das manchmal auch zu tun.

    OK, dann vertraust du vielleicht den Funktionen, mit denen du umgehst. Aber vertraust du dem Nutzer, der dein Programm bedienen will, genauso bedingungslos?

    PS: Und der Vorteil der Exception-Behandlung ist, daß du den Fehler dort behandeln kannst, wo du weißt, wie du reagieren mußt. Wenn du die Rückgabewerte von (z.B.) scanf() nicht sofort auswertest oder speicherst, verschwindet der mögliche Fehler ins Nirvana und du kannst im Nachhinein nicht mehr feststellen, daß etwas passiert ist - und diese Vermischung von Fehlerauswertung und normalem Code ist viel schlimmer als ein geeignet gesetztes try/catch. Mal ein Beispiel:

    //"klassische" Fehlerbehandlung
    while(!ende)
    {
      int scanres;
      ...
      int value;
      scanres = scanf("%d",&value);
      if(scanres!=1)
      {
        fprintf(stderr,"Eingabefehler\n");
        break;
      }
      ...
      //und solche Blöcke können - in verschiedenen Variationen - noch einige Male in der Schleife vorkommen
    }
    
    //Fehlerbehandlung mit Exceptions
    cin.exceptions(ios::failbit|ios::badbit);//Schalte Exceptions für cin an
    try
    {
      while(!ende)
      {
        ...
        int value;cin>>value;
      }
    }
    catch(std::exception& e)
    {
      cerr<<"Eingabefehler "<<e.what()<<endl;
    }
    

    Was sieht jetzt besser aus?


Anmelden zum Antworten