std::auto_ptr vs. smart pointer aus boost::smart_ptr



  • Optimizer schrieb:

    Da setz ich mich dann hin und viiiieeel Zeit geht drauf, bis mir ein gutes System eingefallen ist, wie und wann ich den Speicher am besten freigebe.

    In diesen ganz seltenen Situationen kann man ja Refcounting machen.
    Und nur deshalb (weil man uU einmal in so eine situation kommen koennte (mir ist dies noch nicht passiert)) einen GC fordern? naja...

    Erklaer mal lieber warum man in C++ einen GC braucht? Bisher sind alle Argumente von dir abgeschmettert worden 🙂 faellt noch etwas besseres ein?



  • Refcounting ist keine Garantie für gar nichts (siehe oben). btw, ich bin nicht der Meinung, dass man für C++ einen GC "braucht". "Brauchen" tut man sowieso überhaupt keinen...
    Oder ich würde zumindest für C++ keinen wollen. Die Diskussion war auch ursprünglich überhaupt nicht auf C++ bezogen, sondern auf Arten der Resourcenverwaltung allgemein.
    Ich bin auch wie Hume der Meinung, dass wenn es sich nicht gerade um triviale Resourcen wie RAM handelt, ein GC nicht viel bringt (außer eine Rettung in letzter Not, über finalizer, guter Stil ist das sicher nicht).
    Das Eigentliche, was mich ein bisschen verwirrt ist eine anscheinend vorherrschende generelle Abneigung gegen Laufzeitumgebungen (insbesondere auch unter den Endkunden, siehe ursprüngliche Diskussion über Verbreitung des .Net Frameworks). Für C++ will ich gar keine haben, managed C++ gefällt mir auch kein Stück. Aber darum gings ja auch nicht wirklich. 🙂

    Bisher sind alle Argumente von dir abgeschmettert worden

    Meinst du meine Argumente für garbage collection? wo sind die abgeschmettert worden? Refcounting ist kein Ersatz, scope-Destruktoren schon gleich gar nicht. Die einzigen Gegenargumente, die mir aufgefallen sind, waren, man hätte ja dies und das, aber ich habe glaub ich ausreichend begründet, warum dies und das kein Ersatz ist.



  • Optimizer schrieb:

    die mir aufgefallen sind, waren, man hätte ja dies und das, aber ich habe glaub ich ausreichend begründet, warum dies und das kein Ersatz ist.

    mir ist als begründung nur aufgefallen "aber manche machen dauernd löcher und der gc würde manchmal per finalizer mir was retten".
    die meisten können nicht mal c++, sollten wir nicht besser basic nehmen, weil mehr leute es können?
    falls wir uns drauf einigen können, nicht die leute, die c++ nicht können, zum maß zuz nehmen, warum nicht gleich auch drauf einigen, nicht die leute zu nehmen, die c++ kaum können?



  • Ok, einigen wir uns darauf, dass kein Programmierer jemals wieder einen Fehler machen soll. 🙂

    mir ist als begründung nur aufgefallen "aber manche machen dauernd löcher und der gc würde manchmal per finalizer mir was retten".

    Sollte eher so gemeint sein: "Was man nicht selber machen muss, kann man auch nicht falsch machen".



  • Optimizer schrieb:

    "Was man nicht selber machen muss, kann man auch nicht falsch machen".

    Und genau deshalb, hat man RAII.

    Da muss man nichtmehr freigeben, weil das automatisch geschieht.

    OK, man muss sich merken, dass man RAII verwenden soll - aber dass sollte gerade noch gehen.



  • Was ist RAII? Das beim Verlassen des scopes der Destruktor aufgerufen wird, oder?
    Das kann ich fast nie brauchen. Wenn ich ne Socket-Verbindung aufbaue, dann brauch ich die halt so lange, wie ich sie brauche. Wenn es nicht gerade ein Chat-Client simpelster Art ist, hilft dir das doch kaum. Ich weiss nicht, was du für Erfahrungen gemacht hast, aber bei den meisten meiner Objekte, die relevante Resourcen verwalten, ist die Lebenszeit nicht absehbar -> auf den Heap -> ans delete denken müssen.
    Ein GC löst das Problem natürlich genauso wenig, zumindest rechtzeitig gibt der das bestimmt auch nicht frei. Ich sehe für realistisches Anwendungsfälle keinen Weg, da rum zu kommen, dass der Programmierer das selber in die Hand nehmen muss.



  • Optimizer schrieb:

    Was ist RAII? Das beim Verlassen des scopes der Destruktor aufgerufen wird, oder?

    Nein. RAII steht für Resource Acquisition Is Initialisation und bedeutet soviel, dass eine Resource in einem Konstruktor eines Objekts angefordert wird und im Destruktor freigegeben wird.
    Am effektivsten sind RAII-Objekte, wenn es sich dabei um lokale Objekte handelt. Der Punkt ist aber, dass man pro Resource ein Objekt hat, das diese Resource managed.

    Wenn ich ne Socket-Verbindung aufbaue, dann brauch ich die halt so lange, wie ich sie brauche
    Wenn es nicht gerade ein Chat-Client simpelster Art ist, hilft dir das doch kaum. Ich weiss nicht, was du für Erfahrungen gemacht hast, aber bei den meisten meiner Objekte, die relevante Resourcen verwalten, ist die Lebenszeit nicht absehbar -> auf den Heap -> ans delete denken müssen.

    Dieser Logik kann ich nicht folgen. Normalerweise ist irgendeine Klasse letztlich Besitzer der Socket-Verbindung. Wenn es Zeit wird aquiriert die Klasse die Socket-Verbindung (nicht notwendigerweise im Ctor) und im Destruktor wird die Socket-Verbindung wieder freigegeben. Sollte die Klasse noch andere Resourcen managen, deligiert sie diesen Vorgang an ein RAII-Objekt und hält ein solches als Member.
    Ein Objekt der Klasse ist dann z.B. ein lokales Objekt in main. Oder aber ich habe ein Objekt auf dem Heap und verwalte diese irgendwo (z.B. in main) über einen auto_ptr (oder ähnliches).

    Ich sehe für realistisches Anwendungsfälle keinen Weg, da rum zu kommen, dass der Programmierer das selber in die Hand nehmen muss.

    Mir scheint du hast bisher wenig gute C++ Programme gesehen.



  • Hat das einen Vorteil gegenüber den nichtdeterministischen Destruktoren? Vielleicht brauchst du von den 9832475 Socketverbindungen, die du indirekt verwaltest, die Hälfte schon nach kurzer Zeit nicht mehr. Aber automatisch werden sie auch nur gekickt, wenn du das ganze Chief-Objekt ( 🤡 ) deletest und damit praktisch überhaupt keine mehr hast.
    Macht für mich keinen Unterschied, wenn du nicht daran denkst, die, die du schon früher nicht mehr brauchst, schon vorher freizugeben, kommt auch erst am Schluss in letzter Instanz der Destruktor, der dann alles killt.

    Ich sehe den Vorteil nicht, das kann mein nichtdeterministischer Destruktor am Programmende dann auch und mit hoher Wahrscheinlichkeit schon früher.



  • warnung: dieses posting hat keine relevante aussage. das lesen desselben könnte ermüdend wirken.

    Optimizer schrieb:

    Das kann ich fast nie brauchen. Wenn ich ne Socket-Verbindung aufbaue, dann brauch ich die halt so lange, wie ich sie brauche. Wenn es nicht gerade ein Chat-Client simpelster Art ist, hilft dir das doch kaum.

    kommt drauf an, wie man ansetzt. nehmen wir also keinen simelsten chat-client, sondernen nen simpelsten chat-server.
    der listening socket kann durchaus auf den stack.
    immer, wenn der ne verbindung aufgemacht kriegt, muss was entstehen. naja, ne sitzung halt. die hat vermutlich nen (lebenden!) socket und ein paar userdaten (vielleicht bloß die iser-id).
    dann ist zunächst mal zu prüfen, wer dieses sitzungsobjekt löschen sollte. also mitkriegen tut's zuerst der socket, wenn die sitzung beendet wird. der kann es mit sicherheit der sitzung weitersagen. die sitzung kann jetzt viele wege gehen. zum beispiel das beliebte "delete this;". gruselig. aber welche alternativen bleiben? sie könnte dem globalen verbindungskiller sagen "kill mich, wenn du zeit hast". sie könnte false zurückgeben in der funktion, die irgendein manager aufgerufen hatte (ich gehe ncht von einem thread pro session aus). sie könnte ne exception werfen.
    vermutlich ist hier am saubersten, false zurückzugeben. dann kann leicht derjenige, der false empfangen hat die sitzung löschen. braucht man dazu zeiger auf sitzungen? nein. sie könnten genausogut in nen std::vector reinkonstruiert werden. ob der manager sitzungs-zeiger oder sitzungs-ids (arrayindizes) benutzt, ist ja egal.
    ich fürchte, deine aussage

    Ich weiss nicht, was du für Erfahrungen gemacht hast, aber bei den meisten meiner Objekte, die relevante Resourcen verwalten, ist die Lebenszeit nicht absehbar -> auf den Heap -> ans delete denken müssen.

    ist mir nicht nachvollziehbar.
    ich hab gerade einen programmausdruck vor mir. mein homepage-uploader. die wichtigsten klassen heißen (denke, das aufzählen der klassennamen macht weitgehend klar, wie er innen funktioniert): Internet FtpConnection, CriticalSection, Lock, Semaphore, Event, Job, JobQueue, JobCreateDir, JobSyncDir, JobCreateFile.
    der ablauf ist so, daß einige threads im sekundenabstand gestartet werden, die thread holen sich ober ne durch semaphore gesteuerte queue aufträge, bestimmte aufträge wie JobSyncDir können beim abarbeiten witere aufträge erzeugen (u.a. JobSyncDir für jedes unterverzeichnis).
    also ein wildes umhergewerfe von jobs, sie werden zum größten teil von parallelen threads erzeugt und es wäre sogar nett, wenn sie am ende verschwinden würden. geht natürlich nur über refcounting smart pointers. jeder job merkt sich, welche subjobs er erzeugt hat und hält sie dadrch am leben usw. nee, scherzchen gemacht. ich schreib dir jobs einfach in die globale JobQueue rein und gut ists. die ist auch für's löschen zuständig.
    welche ressourcen bindet ein job? tja, gar keine. die basisklasse Job ist leer und sachen wie JobSyncDir haben halt nen string drin, den verzeichnisnamen. soweizt zu den wichtigen resourcen, die auf dem heap liegen.
    das handle HINTERNET... (man muss bei win für die <wininet.h>-sachen ne inernetverbindung locken) lebt in der main() als auto-variable, jeder thread hat ne eigene ftp-connection, geht auch gut als auto-variable. die jobqueue ist der einfachheit halber global, sie könnte auch in der main leben, und hat ein wenig speicher und ne semaphore. das event zum beenden aller threads ist wieder global.
    nö, alles wichtige legt auf dem stack (oder global) und nix wichtiges ist auf dem heap. zufall?
    der andere ausdruch vor mir ist mein html-generator. da gibts außer speicher nur eine ressource, ein file. ein ifstream auf dem stack.

    so komm ich nicht weiter. ob ich mich daran erinnern kann, daß ich viele wichtige ressourcen auf den heap geworfen hab? nee, eigentlich nicht. also sachen wie dateien, semaphoren, critical sections, und so liegen eigentlich immer auf dem stack. bloß bei sockets sehe ich freispeicher, aber da hat man ja immer ne liste aller offenen sitzungen. dieser liste sage ich dann, daß sie beim sterben die noch offenen verbindungen gleich auch killen soll. will sagen, es ist wenig, daß man mal probleme hätte, zu wissen, wer was löschen muss. die meisten ressourcen liegen eh nicht im freispeicher. die, die doch drin liegen, gehören einer datenstruktur, die nicht drin liegt. fertig eigentlich. letztendlich zieht RAII total gut.
    und du machst ja auch nix anderes. um du auf mene oder auf deine methode dafür sorgst, daß der sterbende container die zeiger alle deleted, ist ja egal. du scheinst allerdings von mehr zeigern auszugehen, die im wald rumstehen und nicht brav in containern liegen. kann ich wieder nicht nachvollziegen, warum man die nicht in nen container stopft.



  • Hallo,
    keiner sagt, dass RAII dich davor schützt dein Gehirn einsetzen zu müssen. Wenn du zu einem Zeitpunkt t eine Resource X nicht mehr brauchst, dann musst du dies schon irgendwo explizit codieren. Zumindest falls t nicht zufälligerweise mit einem Block-Ende zusammenfällt.
    RAII kann aber garantiert dafür sorgen, dass an irgendeinem festen Zeitpunkt t1 alle Resourcen wieder freigegeben werden -> Keine Resource-Leaks.
    Sprich: RAII schützt dich vor Resource-Leaks (also vor dem GAU-Fall) es implementiert aber nicht automatisch eine bestimmte Anwendungslogik.

    Zu deinem Beispiel: Ich würde die einzelnen Verbindungen z.B. durch Connection-Objekte repräsentieren. Diese halte ich alle in einem Container, dessen Dtor alle Connections freigibt -> keine Resource-Leaks.
    Außerdem implemtiere ich in den Connection-Objekten sowas wie ein "destructive close". Wird die Verbindung beendet (entweder vom Client oder vom Server) wird automatisch auch die Resource (z.B. der Socket) freigegeben.
    Das ist aber eine Designentscheidung und eine solche kann dir kein GC dieser Welt abnehmen.

    BTW: RAII ist besonders in Verbindung mit Exceptions sehr wertvoll und der Grund dafür, dass man in C++ a) viel weniger try{}-catch{}-Blöcke sieht als zum Beispiel in Java und b) kein finally braucht.



  • Optimizer schrieb:

    Hat das einen Vorteil gegenüber den nichtdeterministischen Destruktoren? Vielleicht brauchst du von den 9832475 Socketverbindungen, die du indirekt verwaltest, die Hälfte schon nach kurzer Zeit nicht mehr. Aber automatisch werden sie auch nur gekickt, wenn du das ganze Chief-Objekt ( 🤡 ) deletest und damit praktisch überhaupt keine mehr hast.

    falsch.
    natürlich werden sockets immer so früh wie möglich gelöscht. noch in der selben zeitscheibe, wo man vom disconnect was mitgekriegt hat. also gleich, unverzüglich, sofort, unmittelbar und eilig. das cheif-objekt als besitzer hat alle zu löschen, die man nicht vorher selber gelöscht hat, also insbesondere im falle einer exception, die bis oben hin durchschlägt.


Anmelden zum Antworten