frage zu heap allozierung ...



  • Der "Destruktor" eines rohen Zeigers tut nichts.



  • pepe75 schrieb:

    das genau war auch meine schlussfolgerung als ich den code sah (ist fremcode).
    mir ging es eigentlich nur darum zu wissen, ob meine vermutung dass ein memory leak entsteht richtig ist.

    Für solche Fälle ist eine vollständige und aussagekräftige Dokumentation gut. Das nächste Problem, welches du mit diesem Code bekommst ist, wieviele Elemente hat den nun dein allokierter Speicherbereich? An der Stelle wirst du um eine gescheite Dokumentation oder Kapselung nicht mehr rum kommen. Das man den Speicher frei geben muss, ließe sich zur Not noch ermitteln. Auch womit. Wenn es dafür keine extra Funktion gibt. Aber spätestens wenn auch noch ein eigenes Speichermanagement implementiert ist hätte der Autor doch ein wenig dokumentieren sollen.
    Code der keine/wenig Dokumentation hat, weil er sich selbst dokumentiert, halte ich aber prinzipiell für eine gute Idee. Sogar wesentlich sinnvoller als eine Doku zu schreiben.



  • Selbst mit gute Doku werden bei einem solchen Konzept leicht und häufig Fehler gemacht. Es ist einfach zu vermeiden.



  • Es ist einfach zu vermeiden.

    Das ist mir eine zu starke Einschränkung, Pointer sind auch in C++ ein legitimes Werkzeug. Man kann es so machen, dass klar ist wie man damit umgehen muss.

    Ich mach es zB. immer so:

    obj* Object ();
    

    oder

    obj* GetObject ();
    

    der gelieferte Pointer muss nicht entsorgt werden.

    obj* CreateObject ();
    

    der gelieferte Pointer muss mit

    obj->Destroy ()
    

    entsorgt werden.

    Herzliche Grüsse
    Walter



  • Spätestens wenn mehrere Leute dran arbeiten ist es eben nicht mehr so klar, wie es vielleicht einmal sein sollte. Um mal der Chaostheorie zu folgen wird es sicher Probleme geben. Aber wie dem auch sei, spätestens wenn jemand auf den Gedanken kommt deine

    obj* GetObject ();
    

    schön zu verpacken in seine eigene Funktion und wieder das Objekt zurückgibt stehst du wieder vor dem Dilemma. Löschen oder Nicht löschen? Und du musst umständlich in der Doku nachlesen. Durch das fehlende Highlight von new und delete sehen heapobjekte aus als würden sie bad-ptr sein oder auch der eventuelle Gebrauch von delete obj anstelle von obj->Destroy() kann auch zu nicht nachvollziehbaren Fehlern führen.

    Mir fällt auch schlagartig kein einfaches Beispiel ein, wo dies unbedingt notwendig essentiell so gemacht werden muss. Ich möchte nicht sagen, dass man es komplett verteufeln oder verbieten sollte, allerdings vermeiden sollte. Andere Rückgabewerte sind einfach klarer und wartungsärmer.



  • HighLigerBiMBam schrieb:

    Aber wie dem auch sei, spätestens wenn jemand auf den Gedanken kommt deine

    obj* GetObject ();
    

    schön zu verpacken in seine eigene Funktion und wieder das Objekt zurückgibt stehst du wieder vor dem Dilemma.

    Natürlich kann man einen Wrapper schreiben, der die konsistente Semantik wieder durcheinander bringt. Aber davor kann man sich nicht schützen, auch bei Smart-Pointern nicht.

    HighLigerBiMBam schrieb:

    [...] oder auch der eventuelle Gebrauch von delete obj anstelle von obj->Destroy() kann auch zu nicht nachvollziehbaren Fehlern führen.

    Wer einfach so delete auf irgendeinen Zeiger anwendet, hat auch verdient, dass ihm die Anwendung um die Ohren fliegt. Das ist doch kein Grund gegen Zeiger.

    Aber natürlich kann man das Risiko verringern, z.B. durch Benutzung von auto_ptr oder eines besser implementierten verschiebbaren Smart-Pointers. Aber man nimmt dem Benutzer damit halt auch Freiheit. Jedoch finde ich ein Interface schlecht, das den Benutzer zu manueller Speicherverwaltung zwingt. Das führt garantiert früher oder später zu Problemen. Man sollte mindestens noch eine RAII-Möglichkeit anbieten, wie z.B. ein Objekt, das automatisch Destroy() aufruft.

    Ausserdem fände ich eine globale Freigabefunktion im Hinblick auf die globale Erzeugungsfunktion symmetrischer als eine Memberfunktion.



  • Mir fällt auch schlagartig kein einfaches Beispiel ein, wo dies unbedingt notwendig essentiell so gemacht werden muss.

    Es geht auch nicht um die einfachen Beispiele, da bin ich vollkommen mit Dir einverstanden. Aber wir haben zB. Maschinen mit ganz unterschiedlichen Handlingsystemen von manueller Zuführung bis zu SPS gesteuerten Handlern. Da liest die CreateObject() funktion des virtuellen Handlers die Konfiguration (XML Datei) und erzeugt den richtigen Handler.

    Natürlich ist das alles schön gekapselt und man arbeitet im Programm mit Referenzen, aber im Kern gibts die CreateObject () und die Destroy () Funktion.

    Herzliche Grüsse
    Walter



  • Ausserdem fände ich eine globale Freigabefunktion im Hinblick auf die globale Erzeugungsfunktion symmetrischer als eine Memberfunktion

    Nur das Objekt selber weiss wie es sich zerstören muss.

    Man kann da mit viel Aufwand sicher etwas bauen, aber wir Programmierer haben ja ein Gehalt 😉

    Herzliche Grüsse
    Walter



  • Meine Aussagen haben sich mehr auf public-Methoden bezogen. Bei privaten Methoden die innerhalb einer Klasse sich selbst verwalten habe ich wenig gegen Zeiger.

    Die Frage des Autors sah für mich implizit anders aus, dass er eine Klasse verwendet die ein Heap-Objekt nach außen gibt, was ich nicht befürworten kann und will^^



  • weicher schrieb:

    Nur das Objekt selber weiss wie es sich zerstören muss.

    Das ist unlogisch. Das Objekt wurde von einer anderen Funktion erzeugt. Nur die Funktion weiss, wie die Zerstörung zu erfolgen hat, sofern du nicht irgendwelche Querabhängigkeiten hast.

    Beispiel:

    Object* Factory1()
    {
        return new Object();
    }
    
    Object* Factory2()
    {
        void* memory = std::malloc(sizeof(Object));
        new (memory) Object();
        return static_cast<Object*>(memory);
    }
    
    Object* Factory3()
    {
        myObjectList.push_back(Object());
        return &myObjectList.back();
    }
    

    Was schreibst du nun in den Destruktor? Ihr werdet euch zwar auf eine Allokationsmethode festgelegt haben. Aber dass diese nur das Objekt selbst kennt, kann nicht sein, da es sich nicht selbst erzeugen kann (und die Deallokation symmetrisch zur Allokation sein muss).

    Insofern denke ich eher, dass die Symmetrie sogar weniger Aufwand mit sich brächte, weil sich die Allokations- und Deallokationsfunktion im gleichen Modul befinden und du bei einer neuen Implementierung nicht an verschiedenen Stellen Code ändern musst.



  • Ich habe natürlich immer mein Framework im Kopf und da ist das releasen des Speichers der kleinste Teil beim zerstören eines Objekts.

    Da dieser Thread aber nur die heap allozierung behandelt bin ich damit eigentlich schon off Topic. Ich denke nur, man kann das nicht so isoliert betrachten, Objekte sind IMHO viel mehr als nur allozierter Speicher.

    Als ich diesen Thread gesehen habe war ich am implementieren eines Fensters welches in eine DLL ausgelagert ist und da ist die Kombination von Create() und obj->Destroy() die einfachste Möglichkeit das Dingens wieder sauber loszuwerden.

    Ich weiss, das ist jetzt etwas speziell. Aber das ist eigentlich genau der Punkt. Auf die Pauschale ablehnung von Sprachmitteln (über)reagiere ich etwas allergisch 😉

    Kein Schreiner wird je Sagen "Verwende NIE und NIMMER die Kreissäge, damit kannst Du Dir die Finger absägen!". Warum Programmierer genau das immer wieder tun ist mir ein Rätsel. Was passiert denn schon schlimmes wenn mal ein Pointer ins Nirwana zeigt oder man ein Memoryleak einbaut.

    Das Programm stürzt beim ersten vernünftigen Test ab... Es gibt keine Verletzen und es fliesst kein Blut und man hat alle Werkzeuge um den Fehler zu beheben.

    Herzliche Grüsse
    Walter



  • weicher schrieb:

    Man kann es so machen, dass klar ist wie man damit umgehen muss.

    Ich mach es zB. immer so:

    obj* Object ();
    

    oder

    obj* GetObject ();
    

    der gelieferte Pointer muss nicht entsorgt werden.

    obj* CreateObject ();
    

    der gelieferte Pointer muss mit

    obj->Destroy ()
    

    entsorgt werden.

    Ich stimme Dir zumindest in dem Punkt zu, dass man sich überlegen sollte, wie man Funktions-Deklarationen bzgl "Besitz von Objekten" selbstdokumentierent gestalten kann. In C++0x würde ich das so machen:

    Rohe Zeiger: Kein Ownership-Transfer (per Konvention).
    unique_ptr: Ownership-Transfer (ergibt aus dem Verhalten von unique_ptr).

    Also, ohne Ownership-Transfer:

    clazz* get_obj();
    void set_obj(clazz*);
    

    und mit Ownership-Transfer:

    unique_ptr<clazz> get_obj();
    void set_obj(unique_ptr<clazz>);
    

    Gruß,
    kk



  • weicher schrieb:

    Was passiert denn schon schlimmes wenn mal ein Pointer ins Nirwana zeigt oder man ein Memoryleak einbaut.

    Das Programm stürzt beim ersten vernünftigen Test ab...

    Wenn das deine Einstellung ist, hast du in der Diskussion wohl nicht soviel verloren. Durchaus legitim, aber das sehen viele anders, und mit Sicherheit sehr viele von denen, die C++ heute noch die Treue halten (und nicht nur Legacy-Code warten müssen.)
    Wie du ein Speicherleck durch einen Absturz finden willst, musst du aber noch verraten.



  • und nicht nur Legacy-Code warten müssen

    Ich warte nicht nur legacy Code, ich bin daran mein Framework für die aktuelle Compilergeneration praktisch neu zu schreiben und Du würdest staunen wieviele Überprüfungen da eingebaut sind 😉

    Normalerweise gibts bei Memoryleaks keinen Absturz aber auch das habe ich schon erlebt und es dauerte nach dem Start gar nicht so lange bis nichts mehr gieng.

    Aber für Memoryleaks gibt es neben dem Debugger auch noch andere Werkzeuge um sie zu entdecken. Man muss sie nur Anwenden.

    Aber Du verstehst mich falsch, ich sage nicht man soll einfach unbedacht jedes Sprachmittel einsetzen. Wenn es die Möglichkeit gibt etwas abzusichern dann soll man das tun. Aber nach mehr als 30 Jahren Programmieren weiss ich einfach, dass selbst die besten Schutzmechanismen nichts gegen einen unbedachten Programmierer ausrichten können aber vielfach den bedachten und sorgfältigen Programmierer stark behindern.

    Wenn das deine Einstellung ist, hast du in der Diskussion wohl nicht soviel verloren

    Ich habe jetzt auch alles gesagt 😃

    Herzliche Grüsse
    Walter



  • weicher schrieb:

    Das Programm stürzt beim ersten vernünftigen Test ab...

    Schön wäre es.
    Ich hab über Weihnachten einen Bug aus unserer Software entfernt der irgendwann zwischen 1995 und 1998 da rein kam.

    Von den Bugs die mittlerweile ein "Feature" sind weil das Verhalten der Software mittlerweile vom Kunden so erwartet wird, rede ich garnicht erst...

    Leider sind die wenigsten Bugs so nett und zeigen sich sofort.



  • weicher schrieb:

    Normalerweise gibts bei Memoryleaks keinen Absturz aber auch das habe ich schon erlebt und es dauerte nach dem Start gar nicht so lange bis nichts mehr gieng.

    Ja klar, aber du hattest gesagt, Memoryleaks seien kein Problem, weil sie eh auffallen. Das ist meiner Erfahrung nach in der Regel nicht der Fall, kleine Memoryleaks werden einfach hingenommen. Man sucht die in der Praxis nicht akribisch, die einzige Möglichkeit, sie zu beseitigen, besteht darin, sie von vornherein nicht entstehen zu lassen.

    Aber Du verstehst mich falsch, ich sage nicht man soll einfach unbedacht jedes Sprachmittel einsetzen.

    Mag sein, dass du das außerdem gesagt hast. Ich wende mich gegen deine Aussage, Speicherlecks und ungültige Zeiger seien völlig unproblematisch.



  • Ich kann euch da nicht verstehen. Gegen Schweineren auf dem Heap gibt es Werkzeuge. Wieso muss man darüber diskutieren?



  • weicher schrieb:

    Was passiert denn schon schlimmes wenn mal ein Pointer ins Nirwana zeigt oder man ein Memoryleak einbaut.

    Meinst du das ernst?

    weicher schrieb:

    Aber für Memoryleaks gibt es neben dem Debugger auch noch andere Werkzeuge um sie zu entdecken. Man muss sie nur Anwenden.

    Das beste Werkzeug gegen Memory Leaks ist RAII. Man muss es nur anwenden. Wenn man irgendwelche Zusatztools wie valgrind braucht, um Memory Leaks zu beheben, hat man schon längst etwas falsch gemacht. Zumindest im Grossteil der Fälle.

    Eine moderne C++-Anwendung ist memory-leak-frei, ohne dass man speziell etwas dafür tun muss. Dass das bei antikem Code anders aussehen kann, verstehe ich, aber verallgemeinere das bitte nicht.

    weicher schrieb:

    Aber nach mehr als 30 Jahren Programmieren weiss ich einfach, dass selbst die besten Schutzmechanismen nichts gegen einen unbedachten Programmierer ausrichten können aber vielfach den bedachten und sorgfältigen Programmierer stark behindern.

    Vielleicht verstehen wir unter Schutzmechanismen nicht das Gleiche. Aber Konstrukte wie std::tr1::array , std::vector , boost::scoped_ptr machen das Leben allen Programmierern leichter, sofern sie sinnvoll eingesetzt werden.



  • Ich kanns mir doch nicht verklemmen 😉

    Ich habe gesagt: "Was passiert denn schon schlimmes"

    Du hast gelesen:
    seien kein Problem
    seien völlig unproblematisch.

    Was ist schlimm:
    Finger ab.
    Job weg.

    Was ist ein dangling Pointer:
    ärgerlich

    Was ist ein memory leak:
    unangenehm und eine Herausforderung.

    Zumindest im Grossteil der Fälle.

    Eben

    sofern sie sinnvoll eingesetzt werden.

    Eben

    Herzliche Grüsse
    Walter



  • weicher schrieb:

    Was ist ein dangling Pointer:
    ärgerlich

    Zeitraubend, nervenaufreibend, einfach nur unnötig. Gleiches für das Memory Leak. Gerade weil es derart einfache Techniken gibt, um solche Fehler auszumerzen, ist die Mühe einfach nicht gerechtfertigt.

    weicher schrieb:

    Zumindest im Grossteil der Fälle.

    Eben

    Natürlich behauptet jeder, er würde zu den Ausnahmefällen gehören. Ist immer so, auch bei den Spaghetticodern mit goto .

    weicher schrieb:

    sofern sie sinnvoll eingesetzt werden.

    Eben

    Hm, was ist wohl schwieriger. Eine fertige, selbst-verwaltende Klasse einzusetzen oder einen besitzenden Zeiger mit manueller Speicherverwaltung? Eure Memory Leaks und Dangling Pointers liefern da eine klare Antwort, fürchte ich.


Anmelden zum Antworten