init Funktion oder Konstruktor



  • Badestrand_ schrieb:

    Th69 schrieb:

    Auch wenn ich selber selten Init-Funktionen verwenden, aber eure Argumentation hört sich so an, als ob auch die Methode 'clear()' beim std::vector oder std::string etc. sinnlos wäre...

    Ja, warum auch nicht. [...] Und mich würde es nerven, wenn Klassen generell mit so einer clear-Funktion designt werden würden. Weil imho nicht von Mehrwert.

    Natürlich, der Unterschied von Containern und irgendwelchen Klassen ist aber schon wichtig. Der einzige Lebensinhalt von Containerklassen besteht in der Speicherung und Verwaltung von Objekten. Da ist es meiner Meinung nach durchaus angebracht, sämtliche Objekte auf einmal zu löschen. Schliesslich sind diese eigenständig und haben nicht direkt mit dem Status des Containers zu tun. Zumindest ist die Kopplung Container <-> Element viel geringer als Klasse <-> Membervariable. Ob freie Funktion oder Member ist wieder eine andere Frage, spielt aber im Bezug auf die bereitgestellte Funktionalität keine entscheidende Rolle.

    Im Weiteren wird bei Containerklassen beim Aufruf von clear() tatsächlich eine Leerung durchgeführt. Es existieren nach der Operation meistens weniger Elemente als vorher. Ein Objekt selbst hingegen kann man nicht leeren, denn man kann eine Entität an sich nicht löschen. Die Löschung geschieht immer im Bezug auf die Umgebung, welche das eigentliche Objekt referenziert – sei es ein Zeiger bei delete , ein Container bei erase() , oder irgendwas (das gilt im Übrigen nicht nur für C++).

    Genau aus diesem Grund ist ein allgemeines clear() sinnlos. Was sollte es tun? Das Objekt in den Ausgangszustand versetzen? Das ist oft gar nicht möglich, ausserdem sieht C++ für die Überschreibung des gesamten Objekts Zuweisungsoperatoren vor. Bei Containern sieht es jedoch wie erwähnt anders aus.


  • Administrator

    Th69 schrieb:

    Auch wenn ich selber selten Init-Funktionen verwenden, aber eure Argumentation hört sich so an, als ob auch die Methode 'clear()' beim std::vector oder std::string etc. sinnlos wäre...

    Was hat bitte die clear Funktion mit einer init Funktion zu tun? Das sind doch zwei ganz verschiedene paar Schuhe. Das ist als wenn du push_back mit init vergleichen würdest. Ich kann jedenfalls keinen Zusammenhang erkennen.

    Ich bin auch gegen öffentliche init Funktionen. Dafür ist der Konstruktor da. Es ist höchstens zum Teil die Frage da, ab wann ein Objekt initialisiert ist? Typisches Beispiel hierfür sehe ich bei boost::thread . Nach der Konstruktion des Objektes startet der Thread bereits. Da hätte ich nichts gegen eine launch oder start Methode einzuwenden gehabt, da ich dies nicht mehr als zur Initialisierung gehörend empfinde.

    Grüssli



  • Bei komplexen Objekten, die Function-Calls machen müssen, um in einem benutzbaren Zustand zu gelangen, verwende ich immer init-Funktionen und ich verwende meist ein HRESULT als Rückgabewert, der dann auch geprüft wird.
    Im Konstruktor setzte ich meist alle Membervar's auf NULL bzw. 0.

    Konstruktoren haben keine Rückgabewerte. Daher ist es bescheuert in Konstruktoren Function-Calls zu machen. Scheitert ein solcher Fucntion-Call, dann gibt es nur noch die Möglichkeit eine Exception ausm Konstruktor zu werfen.

    Init-Funktionen sind bei mir immer public, da sie eben wegen der Rückgabeproblematik nicht aus dem Konstruktor heraus aufgerufen werden können.

    Ich machs meist so:

    bool MyFunction()
    {
       MyObject1 obj1;
       if(FAILED(obj1.init())
       {
          logger.log("Fehlermeldung xxxx");
          return false;
       }
    
       MyObject2 obj2;
       if(FAILED(obj2.init())
       {
          logger.log("Fehlermeldung yyyy");
          return false;
       }
    
       return true;
    }
    

    FAILED ist ein Macro, dass prüft, ob ein HRESULT "ok" ist. So reicht ein Blick in die log-Datei und ich weiß, wo ein init() fehlgeschlagen ist.



  • Dravere schrieb:

    Ich bin auch gegen öffentliche init Funktionen. Dafür ist der Konstruktor da. Es ist höchstens zum Teil die Frage da, ab wann ein Objekt initialisiert ist? Typisches Beispiel hierfür sehe ich bei boost::thread . Nach der Konstruktion des Objektes startet der Thread bereits. Da hätte ich nichts gegen eine launch oder start Methode einzuwenden gehabt, da ich dies nicht mehr als zur Initialisierung gehörend empfinde.

    Sehe ich auch so. Für mich ist ein Objekt initialisiert, wenn die Invarianz gegeben ist und das Objekt korrekt benutzt werden kann.
    In dem Sinne wäre ich auch eher dafür, dass boost::thread nicht gleich im ctor starten würde, sondern das nur als Überladung anbietet, um den in manchen Fällen unnötigen launch zu vermeiden. (Eigentlich so ähnlich, wie bei std::ifstream .
    Vor allem finde ich, dass Klassen eine Möglichkeit anbieten sollten, dass man ein Objekt auf den Zustand, welches es gerade nach der Initialisierung hatte zurückzusetzen. (also bei thread auch einen thread starten, ohne das Objekt zerstören zu müssen).
    Leider geht das nicht immer so einfach.

    @Hans_Guck_In_Die_Luft
    Ich denke, dass du zu fest in C verwurzelt bist und den Sinn von Exceptions nicht so ganz begriffen hast.
    Dein Beispiel kann mit exception viel schöner und ohne Fehlerbehandlung in eigentlichem Produktivcode geschrieben werden:

    void MyFunction()
    {
       MyObject1 obj1;
       MyObject2 obj2;
    
       // das funktioniert hier sicher richtig, weil obj1 und obj2 
       // bestimmt korrekt erzeugt hätten werden können.
       // Bei dir kann man das nicht so einfach machen..
       obj2.foo ( obj1 );
    }
    

    Wenn du allerdings trotzdem etwas auf einen Fehler reagieren kannst, ohne den Standardhandler zu benutzen, dann kannst du das dennoch tun:

    void MyFunction()
    {
       try
       {
         MyObject1 obj1;
         MyObject2 obj2;
    
         obj2.foo ( obj1 );
       }
       catch ( /* je nach dem*/ )
       {
         // ok, ein Objekt konnte nicht erstellt werden, also machen wir 
         // was anderes
       }
    }
    

    Alleine schon das umschreiben der Funktion sieht am Ende besser aus, als deine Variante:

    bool MyFunction()
    {
       try
       {
         MyObject1 obj1;
         MyObject2 obj2;
       }
       catch (..)
       {
          logger.log("Fehlermeldung xxxx");
          return false;
       }
       return true; // ok, scheint alles in Ordnung gewesen zu sein
    }
    

    Und jetzt sag mir, dass das in einer Funktion, die nicht nur 2 Zeilen eigentlichen Code hat, sondern vielleicht 10-20 nicht übersichtlicher wird.



  • @drakon:

    Dein Ansatz führt zum inflationären Gebrauch von Exceptions. Jede Klasse, deren
    Instanzen Function-Calls machen müssen, um benutzbar zu sein, muss dann
    Exceptions werfen. Das ist meiner Meinung nach sicher nicht der Sinn von
    Exceptions. Dafür hat Gott return-values geschaffen.

    Ein weiteres Argument für init() Methoden ist, dass der this-Pointer erst gültig
    ist, wenn der Konstruktor verlassen wurde. In nem Konstruktor sollte man nie
    einen Function-Call mit "this" als Parameter machen.

    In ner init() Methode geht das aber sehr wohl.


  • Mod

    Hans_Guck_In_Die_Luft schrieb:

    @drakon:

    Dein Ansatz führt zum inflationären Gebrauch von Exceptions. Jede Klasse, deren
    Instanzen Function-Calls machen müssen, um benutzbar zu sein, muss dann
    Exceptions werfen. Das ist meiner Meinung nach sicher nicht der Sinn von
    Exceptions.

    Warum?

    Hans_Guck_In_Die_Luft schrieb:

    Ein weiteres Argument für init() Methoden ist, dass der this-Pointer erst gültig
    ist, wenn der Konstruktor verlassen wurde. In nem Konstruktor sollte man nie
    einen Function-Call mit "this" als Parameter machen.

    Unfug.

    Hans_Guck_In_Die_Luft schrieb:

    In ner init() Methode geht das aber sehr wohl.

    Nur dass du this dann immer noch auf ein nicht richtig initialisiertes Objekt zeigt und sich die aufgerufene Funktion nicht auf die Einhaltung der Objektinvarianzen verlassen kann. Ich sehe hier keinen Unterschied zum Aufruf aus Konstruktoren heraus.



  • Hans_Guck_In_Die_Luft schrieb:

    Ein weiteres Argument für init() Methoden ist, dass der this-Pointer erst gültig
    ist, wenn der Konstruktor verlassen wurde. In nem Konstruktor sollte man nie
    einen Function-Call mit "this" als Parameter machen.

    Camper hats bereits gesagt, dass das Unfug ist. Wenn das stimmen würde, würde das heissen, dass man auch keine Memberfunktionen aus dem Konstruktor aufrufen darf und dann wäre so ziemlich die komplette Standardbibliothek falsch oder was auch immer du meinst das das ist..

    Es bestehen gefahren, wie wenn du z.B über den this Zeiger (natürlich auch sonst) auf ein Objekt zugreifst, dass noch nicht initialisiert wurde, daher ist der Gebrauch des this Zeigers micht Vorsicht zu geniesen ( vor allem in der Initialisierungsliste), abre das rührt hauptsächlich daher, dass das Objekt, dass den Zeiger benutzt ev. meint, dass das Objekt bereits besteht. Aber den Zeiger einfach speichern ist kein Problem.
    Ein zweites Problem könnte sein, dass der this Zeiger gebraucht werden könnte, um (auch wieder unvorsichtigerweise) einen polymorphen Funktionsaufruf zu machen und das kann ebenfalls in die Hose gehen. (wird es auch).

    Der Konstruktor ist dazu da, um die Invarianz zu erstellen und ggf. um ein paar Parameter zu setzen und Berechnungen zu machen, welche sonst über Memberfunktionen gleich nach der Erstelltung aufgerufen werde müssten. (also rein syntax sugar).



  • Ok, die Wahrheit liegt in der Mitte:

    http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7

    this im C'tor benutzen geht manchmal. Manchmal isses gefährlich. Meiner Meinung kann man einfach drauf verzichten. Habe noch nie Code von guten Leuten gesehen, die das machen.

    @drakon:

    Dein Ansatz führt zum inflationären Gebrauch von Exceptions. Jede Klasse, deren
    Instanzen Function-Calls machen müssen, um benutzbar zu sein, muss dann
    Exceptions werfen. Das ist meiner Meinung nach sicher nicht der Sinn von
    Exceptions.
    Warum?

    Exceptions sind Ausnahmen. Willste jedes mal, wenn ein Pointer, der NULL ist,
    jedoch nicht NULL sein darf, ne Exception werfen? Exceptions sind meiner
    Meinung nach für Sachen, falls man kein File-Handle bekommt, die Socket-
    Connection nicht funzt oder der Drucker nicht antwortet. Also für Sachen, deren
    Ursache außerhalb der eigentlichen Applikation liegt. Für interene "Fehler" wie
    z.B. Mutex ist blockiert oder eigene Invarianten sind verletzt, kann man einfach Rückgabewerte verwenden.


  • Administrator

    Hans_Guck_In_Die_Luft schrieb:

    Willste jedes mal, wenn ein Pointer, der NULL ist, jedoch nicht NULL sein darf, ne Exception werfen?

    Wieso erlaubst du dann überhaupt erst die Übergabe von Zeigern? Dann kannst du auch eine Referenz verlangen. Und schon musst du diese Überprüfung nicht mehr machen.
    Finde es aber sehr interessant, dass du dies als Argument bringst. Lässt mich auch sehr vermuten, dass du viel zu stark in C denkst.

    Es ist zudem auch die Aufgabe des Programmierers, richtige Werte zu prüfen, welche an den Konstruktor übergeben werden. Wenn dann trotzdem falsche Werte übergeben wurden, dann ist eine Exception gerechtfertigt. Womöglich sogar ein assert .

    Wenn die Konstruktion eines Objektes schief läuft, dann ist wirklich etwas sehr schief gelaufen, was eine Exception völlig rechtfertigt.

    Grüssli



  • Hans_Guck_In_Die_Luft schrieb:

    Ok, die Wahrheit liegt in der Mitte:

    http://www.parashift.com/c++-faq-lite/ctors.html#faq-10.7

    this im C'tor benutzen geht manchmal. Manchmal isses gefährlich. Meiner Meinung kann man einfach drauf verzichten. Habe noch nie Code von guten Leuten gesehen, die das machen.

    Das ist ja genau das, was ich geschrieben habe.
    Ob man this braucht oder nicht kommt halt drauf an. Implizit brauchen den sehr viele. Explizit eher selten. Ich brauche die explizite Variante eigentlich nur an einem Ort, wo sich ein Objekt selbst bei einem Singleton registriert und dann im Destruktor wieder abmeldet. Da finde ich das noch ganz praktisch this im Konstruktor zu benutzen.

    Exceptions sind Ausnahmen. Willste jedes mal, wenn ein Pointer, der NULL ist,
    jedoch nicht NULL sein darf, ne Exception werfen?

    Wenn irgendwo, wo ein Zeiger nicht 0 sein darf 0 ist, dann ja. Wenn du da mal in üblichen Code schaust, dann wirst du sehen, dass da ein assert ist. Ich würde eine Version bevorzugen, die eine Exception wirft. Dann kann man da nämlich ein paar Dinge auch noch im Release Mode stehen lassen und kriegt allenfalls eine gute Meldung und nicht einfach nur einen Absturz.

    Ich stelle mal (wieder) die Brück zu Eiffel, wo Invarianten und Preconditions (und auch Postconditions) tief in der Sprache verwurzelt sind.
    Wenn eine Invariante oder aber auch Precondition nicht eingehalten wird, dann sollte das dem Programmierer an den Kopf springen so, dass er das nicht ignorieren kann. Und da sind Exceptions einge gute Möglichkeit.



  • Ich bin der Meinung, dass die Verwendung von Exceptions um Programmierfehler zu
    finden eine Vergewaltigung des Exception-Konzeptes ist. ASSERT's sind da das
    Mittel der Wahl. Letzendlich gibt es hier aber kein "richtig" und kein "falsch".

    Fehlerbehandlung ist einfach eine Designfrage. Hauptsache man macht es konsequent
    und fängt nicht an hier mal Exceptions zu werfen, dann mit Message-Box-Debug-
    Ausgaben zu arbeiten, in der nächsten Klasse 200 Asserts zu verwenden und in ner
    anderen Klasse 30 Fehlercodes mit #define einzuführen.

    Am Ende des Tages läuft es auf die Frage hinaus:

    "Wer ist verantwortlich für den Zustand eines Objektes? Der Caller oder der
    Callee?" Da es wegen einiger C++ Sprachfeautures (Konstruktoren-Konzept,
    this-Pointer-Problematik) oft problematisch ist, dass ein Objekt selbst dafür
    sorgt, dass es korrekt initialisiert ist, hat sich der Ansatz bewährt, dass der
    Caller guckt, ob er ein sinnvoll initialisiertes Objekt vorfindet. Und dafür sind
    init() Methoden eine unkomplizierte Sache. Warum ein init() Aufruf so verpöhnt zu
    sein scheint, ist mir nicht ganz klar.


  • Administrator

    Hans_Guck_In_Die_Luft schrieb:

    Fehlerbehandlung ist einfach eine Designfrage.

    Jein, es hängt auch stark mit den Möglichkeiten der Sprache zusammen. Eine C Fehlerbehandlung in C++ zu machen, halte ich einfach nur für verkehrt.

    Hans_Guck_In_Die_Luft schrieb:

    "Wer ist verantwortlich für den Zustand eines Objektes? Der Caller oder der Callee?" Da es wegen einiger C++ Sprachfeautures (Konstruktoren-Konzept, this-Pointer-Problematik) oft problematisch ist, dass ein Objekt selbst dafür sorgt, dass es korrekt initialisiert ist, hat sich der Ansatz bewährt, dass der Caller guckt, ob er ein sinnvoll initialisiertes Objekt vorfindet.

    Aber das ist absoluter QUATSCH. In C++ hat sich dieser Ansatz überhaupt nicht bewährt. Modernes C++ verwendet keine init Methoden, sondern direkt den Konstruktor. Es gibt gar keinen Grund für eine init Methode. Sowas ist nur in C von nöten, da man dort keine Konstruktoren hat. Und da man keine Exceptions in C hat, verwendet man dort auch den Rückgabewert der Funktion.

    Du solltest dich vielleicht ein wenig mehr mit den Konzepten in C++ auseinander setzen. Mal rein zum starten, würde ich diese 3 Artikel empfehlen:
    Exception-Handling
    Modernes Exception-Handling: Die Grundlagen
    Modernes Exception-Handling: Hinter den Kulissen

    Vor allem die letzten beiden Artikel, dürften für dich interessant sein.

    Grüssli



  • Hans_Guck_In_Die_Luft schrieb:

    Da es wegen einiger C++ Sprachfeautures (Konstruktoren-Konzept,
    this-Pointer-Problematik) oft problematisch ist, dass ein Objekt selbst dafür
    sorgt, dass es korrekt initialisiert ist, hat sich der Ansatz bewährt, dass der
    Caller guckt, ob er ein sinnvoll initialisiertes Objekt vorfindet. Und dafür sind
    init() Methoden eine unkomplizierte Sache.

    Das stimmt überhaupt nicht. Mit Exceptions hat man im Gegensatz zu Init() -Methoden mit bool -Rückgabewerten die Möglichkeit, bereits die Konstruktion eines Objektes abzubrechen. So kann man verhindern, dass Zombie-Objekte entstehen, die einen undefinierten Status haben. Es gibt natürlich Ausnahmen, in denen eine verzögerte Initialisierung sinnvoll ist. Aber im Allgemeinen hat ein Konstruktor nur Vorteile.

    Was genau ist eigentlich am "Konstruktoren-Konzept" von C++ problematisch?

    Hans_Guck_In_Die_Luft schrieb:

    Warum ein init() Aufruf so verpöhnt zu
    sein scheint, ist mir nicht ganz klar.

    Weil er moderne, objektorientierte Konzepte wie Klasseninvarianzen nicht berücksichtigt und den Programmierer zu C-ähnlichem Stil zwingt, wo manuell initialisiert (und freigegeben) werden muss. Benutzt du auch öffentliche Cleanup() -Methoden statt Destruktoren? Konsequent wäre es zumindest.



  • Hans_Guck_In_Die_Luft schrieb:

    Da es wegen einiger C++ Sprachfeautures (Konstruktoren-Konzept,
    this-Pointer-Problematik) oft problematisch ist, dass ein Objekt selbst dafür
    sorgt, dass es korrekt initialisiert ist, hat sich der Ansatz bewährt, dass der
    Caller guckt, ob er ein sinnvoll initialisiertes Objekt vorfindet.

    Was für ein bildhaft schöner Schwachsinn!

    Du hast gerade RAII "rückwärts-erfunden" 🙄

    Leute gibts...

    Es gibt keine "this-Pointer-Problematik", und es ist auch nicht problematisch, ein Objekt selbst dafür verantwortlich zu machen, dass es korrekt initialisert ist. Im Gegenteil: das ist die einzige Art wie man sinnvoll C++ programmieren kann.



  • hustbaer schrieb:

    Du hast gerade RAII "rückwärts-erfunden" 🙄

    👍

    Ich denke das triffts recht genau.



  • Das stimmt überhaupt nicht. Mit Exceptions hat man im Gegensatz zu Init()-Methoden mit bool-Rückgabewerten die Möglichkeit, bereits die Konstruktion eines Objektes abzubrechen. So kann man verhindern, dass Zombie-Objekte entstehen, die einen undefinierten Status haben. Es gibt natürlich Ausnahmen, in denen eine verzögerte Initialisierung sinnvoll ist. Aber im Allgemeinen hat ein Konstruktor nur Vorteile.

    Hier mal ein Gegenbeispiel. Der Konstruktor alloziert Speicher. Danach kommt ne Exception geflogen, weil die Datei nicht geöffnet werden kann. Der Destruktor wird nie aufgerufen und man hat nen fetten Memory Leak.

    class CMyObject
    {
       double * m_pArray;
       std::fstream m_ofs;
    
       //...
    };
    
    CMyObject::CMyObject()
    { 
       m_pArray = new double[100];   
       m_ofs.open("C:\test.txt");
       if (!m_ofs)
          throw std::exception("(ofstream) error while opening file.");
    }
    
    CMyObject::~CMyObject()
    {   
       if(m_pArray)
       {
          delete m_pArray;
          m_pArray = NULL;
       }
       m_ofs.close();
    }
    


  • deshalb sorgt man ja auch dafür, dass das im ctor nicht geschieht... und wenn doch ist man - logischerweise - selbst dafür verantwortlich... dann muss man halt noch ein try im ctor um den code packen und sich im catch selbst darum kümmern, dass alles wieder freigegeben wird, was man dynamisch angefordert hat - und schmeißt die exception dann halt einfach weiter...

    bb



  • Blödes Beispiel.

    class my_object
    {
        std::vector<double> array;
        std::ofstream ofs;
    public:
        my_object() : array(100), ofs("test.txt")
        {
            if(!fs)
                throw std::exception("(ofstream) error while opening file.");
        }
    };
    

    Mehr brauchts nicht.



  • Hans_Guck_In_Die_Luft schrieb:

    Hier mal ein Gegenbeispiel. Der Konstruktor alloziert Speicher. Danach kommt ne Exception geflogen, weil die Datei nicht geöffnet werden kann. Der Destruktor wird nie aufgerufen und man hat nen fetten Memory Leak.

    class CMyObject
    {
       double * m_pArray;
       std::fstream m_ofs;
       
       //...
    };
    
    CMyObject::CMyObject()
    { 
       m_pArray = new double[100];   
       m_ofs.open("C:\test.txt");
       if (!m_ofs)
          throw std::exception("(ofstream) error while opening file.");
    }
    
    CMyObject::~CMyObject()
    {   
       if(m_pArray)
       {
          delete m_pArray;
          m_pArray = NULL;
       }
       m_ofs.close();
    }
    

    Sorry, aber das ist wirklich schlechter Code. Alleine

    if(m_pArray)
       {
          delete m_pArray;
          m_pArray = NULL;
       }
       m_ofs.close();
    

    zeigt, dass du etliche Konzepte von C++ nicht verstanden hast. In deinem Destruktor ist jede einzelne Anweisung fragwürdig. Auch die Ungarische Notation ist ein Hinweis auf veraltete Stilrichtlinien.

    • Zeiger müssen vor delete nicht auf Null geprüft werden.
    • Dynamische Arrays gibt man mit delete[] und nicht delete frei.
    • Das Nullsetzen nach dem delete ist absolut sinnlos hier.
    • m_ofs.close() ist auch unnötig, weil std::fstream::~fstream() die Datei automatisch schliesst.

    Ich möchte dir jetzt nicht das ganze RAII-Konzept erklären. Aber grundsätzlich musst du deinen Konstruktor eben so konzipieren, dass er bei Fehlschlag aufräumt. Das kannst du manuell machen, aber wenn du moderne Dinge wie STL-Container einsetzt, ist das alles kein Problem. Ein new double[100] öffnet sowieso nur eine Wand von Problemen ohne wirkliche Vorteile. Du bist auf 100 Elemente beschränkt, verschwendest bei nur 2 Elementen Speicher, ausserdem hast du ohne Kopierkonstruktor und Zuweisungsoperator wieder Probleme (hier hast du Glück, dass die unkopierbaren Streams Kopiersemantik unterbinden).

    Wenn du das sauber implementierst, brauchst du nicht einmal einen Destruktor. Siehe Ryuzaki. Ich rate dir, die von Dravere geposteten Links zum Exception-Handling durchzulesen.



  • @Hans_Guck_In_Die_Luft
    Das was du da machst ist mit RAII absolut kein Problem.

    class CMyObject
    {
       boost::scoped_array<double> m_pArray;
       std::fstream m_ofs;
    
       //...
    };
    
    CMyObject::CMyObject()
    :
    m_pArray ( new double[100] ),
    m_ofs ( "C:\test.txt" )
    {
       if (!m_ofs)
          throw std::exception("(ofstream) error while opening file."); // m_pArray
    }
    
    CMyObject::~CMyObject()
    {  
    /* das braucht es alles gar nicht. Im übrigen:
       if(m_pArray) // unsinn
       {
          delete m_pArray; // falsch
          m_pArray = NULL; // für was? willst du m_pArray mach dem dtor noch benutzen?
       }
    */
    //   m_ofs.close(); // auch das ist überflüssig, da wenn m_ofs zerstört wird automatisch das objekt korrekt geschlossen wird
    }
    

    Ich habe immmer mehr das Gefühl, dass du einfach nur C in C++ machen willst und dazu ein wenig eine schönere Syntax haben willst, um Objektorientiert zu arbeiten.

    Das ist definitiv der falsche Weg und wie du siehst ist der Code leichter verständlich, vermeidet es unnötige Fehlerbehandlung vorzeitig zu machen und ist auch noch einfacher zu verwenden.
    Du scheinst die einige Sachen in C++ nicht zu kennen, die das arbeiten sehr viel leichter machen und man sich eben nicht um die Dinge kümmern muss, die du ständig nennst. (Initialisierungsliste, RAII usw.).

    Wie du siehst gibt es für das Beispiel noch andere Möglichkeiten. Ich wollte nur mal zeigen, wie du das alleine durch RAII (und die Trennung der Aufgaben der Klassen) schon sehr viel besser machen kannst.


Anmelden zum Antworten