SmartPointer Design Frage



  • knivil schrieb:

    Dann aendere es. Das ist naemlich im Sinne der Symmetrie schlecht.

    Mir ehrlich gesagt ziemlich egal, was du mit deiner blöden Symmetrie hast. Jeder Reference Counting SmartPtr arbeitet so und fertig.

    Also ist es doch eine Faktory! Auch ist mir egal wie Microsoft und COM das macht.

    Mir aber nicht, da ich mit COM arbeite. Außerdem ist eine Objektzerstör Funktion völlig überflüssig, wenn man bereits Ref Counting hat.

    Nein! Sondern so wie ich es beschrieben habe. Methoden fuer Objecterzeugung/-vernichtung und Referencecounting entkoppeln.

    Ich würde gerne noch die Meinung ANDERER User hier hören:)



  • Ganz spontan würde ich "createFoo" einen SmartPointer zurückgeben lassen. Oder was spricht dagegen (Reference-Counting ist ja sowieso in den Objekten eingebaut, nicht im Zeiger)?



  • Ich würde gerne noch die Meinung ANDERER User hier hören:)

    Sorry, das ich dein Design nicht so toll finde, aber wenn du nur fuer dein Design gratuliert werden und nicht ueber Aenderungen nachdenken moechtest, bitte ... Da bin ich wirklich der falsche. Auch verbiete ich hier niemanden den Mund.

    Desweiteren:

    pd3dDevice->Release();
    pd3dDevice = NULL;
    

    Google war mein Freund.



  • Badestrand schrieb:

    Ganz spontan würde ich "createFoo" einen SmartPointer zurückgeben lassen. Oder was spricht dagegen (Reference-Counting ist ja sowieso in den Objekten eingebaut, nicht im Zeiger)?

    Reference Counting ist bereits in allen Objekten eingebaut ja. Der Smart Pointer würde lediglich das Reference Counting automatisieren (wenn der Smart Pointer den Scope verlässt, ruft er DecRef() auf usw.)

    Ich tendiere mittlerweile auch dazu einen SmartPtr zurückzugeben. Der einzige Nachteil wäre eben, dass der Benutzer nicht mehr ganz so elegant auch ohne SmartPtr arbeiten könnte. Wenn er ohne SmartPtr arbeiten will, würde das dann ja quasi so aussehen:

    SmartPtr<Foo> createFoo();
    
    SmatrPtr<Foo> p = createFoo();  // ref count == 1
    Foo* f = p.get();               // get() liefert das Objekt, auf das SmartPtr zeigt
    f->IncRef();                    // ref count 2. Wird benötigt, weil ja p DecRef() aufruft, wenn es den Scope verlässt
    

    Is also möglich, aber halt net ganz sooo schön.



  • knivil schrieb:

    Desweiteren:

    pd3dDevice->Release();
    pd3dDevice = NULL;
    

    Google war mein Freund.

    Und inwiefern is das hier relevant?
    Release() entspricht 1:1 meinem DecRef(). Es gibt ein createDevice() aber kein destroyDevice().



  • Und inwiefern is das hier relevant?

    Du hast mir dem Vergleich COM angefangen, nicht ich.

    Jeder Reference Counting SmartPtr arbeitet so und fertig.

    Erstens: Das ist falsch. Mir ging es aber auch nicht um den SmartPointer, der kann ruhig das Objekt vernichten, wenn der Zaehler auf null faellt, DecRef() aber nicht.



  • [quote="knivil"]
    Du hast mir dem Vergleich COM angefangen, nicht ich./quote]
    lol?? Was isn das für eine "Logik"? Du kamst damit an, dass die Factory ein destroy() anbieten soll. Dann habe ich gesagt das ich das nicht so machen will und COM als Beispiel genannt, dass das so macht wie ich auch. Und jetzt zitierst du willkürlich ein COM Code Schnipsel, das nur das zeigt was ich bereits gesagt habe. Omg...

    Mir ging es aber auch nicht um den SmartPointer, der kann ruhig das Objekt vernichten, wenn der Zaehler auf null faellt, DecRef() aber nicht.

    Du hast offenbar noch immer nicht verstanden, dass Reference Counting auch ohne Smart Pointer funktioniert. Siehe COM. Du solltest dir wirklich mal boost::intrusive_ptr anschauen, bevor du hier weiter nervst.

    Wie gesagt, über Meinungen ANDERER User freu ich mich dennoch;)



  • marld schrieb:

    Ich tendiere mittlerweile auch dazu einen SmartPtr zurückzugeben. Der einzige Nachteil wäre eben, dass der Benutzer nicht mehr ganz so elegant auch ohne SmartPtr arbeiten könnte.

    Er kann ja so oder so nicht ohne SmartPtr arbeiten, da Reference-Counting und Creation/Deletion ja verschmolzen sind (das ist eigentlich nicht sonderlich schön, da muss ich knivil absolut zustimmen).
    Ich würde jedenfalls entweder voll auf SmartPtr setzen oder die beiden Konzepte entkoppeln. Ein Zwischending funktioniert imho nicht gut.



  • Wo ist das denn bitte verschmolzen? Der Benutzer kann vollständig ohne SmartPtr arbeiten.



  • marld schrieb:

    Wo ist das denn bitte verschmolzen? Der Benutzer kann vollständig ohne SmartPtr arbeiten.

    Foo* f = createFoo(2);  // Foo hat ref count == 1
    // Benutze foo
    f->DecRef();    // Foo ref count == 0 => delete
    

    Das DecRef ist doch ein SmartPtr-Ding. Ohne SmartPtr sähe es so aus:

    Foo* f = createFoo(2);  // Foo hat ref count von "undefiniert"
    // Benutze foo
    destroyFoo( f );
    

    Und konsequent mit SmartPtr so:

    SmartPtr<Foo> f = createFoo();
    // Benutze foo
    // Hier wird automatisch abgeräumt
    


  • marld schrieb:

    Wo ist das denn bitte verschmolzen? Der Benutzer kann vollständig ohne SmartPtr arbeiten.

    dann halt intrisive. wenn die klasse die intrusion über sich ergehen ließ, dann soll die factory auch einen smart pointer zurückgeben und nichts anderes. der empfänger des smart pointers und besitzer des objekts kann dann immernoch entscheiden, daß er an seine diener rohe zeiger verschickt, weil er eh der besitzer bleiben will.
    normalerweise werden eh nur klassen smartpointerbefähigt, die vermutlich herumgeschubst werden, wie netzwerkpakete oder tokens oder jobs.
    außerdem ist ja noch das

    Packet* packet=factory.create();//kein smart pointer, ich bin ein berliner tor
    foo(); //dadrin passiert ein throw
    //speicherloch
    


  • Badestrand schrieb:

    Das DecRef ist doch ein SmartPtr-Ding.

    Nein, eben nicht! DecRef() und IncRef() sind lediglich 2 Methoden, die eine Oberklasse Object anbietet, von der alle Klassen ableiten. Das hat NICHTS mit einem Smart Pointer zu tun!

    volkard schrieb:

    dann halt intrisive. wenn die klasse die intrusion über sich ergehen ließ, dann soll die factory auch einen smart pointer zurückgeben und nichts anderes

    Japp, so werd ichs jetzt auch machen.



  • Foo* createFoo(int i) { 
       Foo* p = new Foo(i); 
       p->IncRef();   // Ref count == 1 
       return p; 
    }
    
    // Anwendung: 
    Foo* f = createFoo(2);  // Foo hat ref count == 1 
    // Benutze foo 
    f->DecRef();    // Foo ref count == 0 => delete
    

    Dafür refCounting ist doch echt nicht nötig...
    Foo produziert immer neue Objekte, also eine nicht denkende, blöde factory.
    Diese werden dann benutzt. Werden die auch irgendwie weitergegeben? Also im Kontext einer Klasse per getter angeboten? Dann macht ReferenceCounting schon Sinn. Ansonsten ist es nur unnötiger Ballast, und hat mMn nix in der Klasse selber zu suchen.
    Außerdem, wenn RefCounting, dann automatisiert. Wenn ein Benutzer deiner Klasse(n) vergisst, die per createFoo erzeugten Objekte per DecRef() freizugeben, oder am Ende meint "Ich hab Pointer, weg damit -> delete foo;", hast du ziemlichen Stress. Memleaks oder SegFault.

    Darum würde ich den RefCounter aus dem Objekt rausnehmen und entweder nur Pointer produzieren, und dem User das freigeben überlassen, oder eben gleich nen SmartPointer zurückgeben.

    Ich tendiere eher zu normalen Pointern.



  • f->DecRef();    // Foo ref count == 0 => delete
    

    Wie wird denn das Objekt vernichtet? Vielleicht so:

    delete this
    

    Du bist verdammt! 🙂



  • knivil schrieb:

    f->DecRef();    // Foo ref count == 0 => delete
    

    Wie wird denn das Objekt vernichtet? Vielleicht so:

    delete this
    

    Du bist verdammt! 🙂

    Ja klar so. Wo soll das Problem sein?



  • Ich finde es ehrlich gesagt auch unschön, über

    Foo* f = createFoo(2);
    

    eine Instanz zu erstellen, die man mit

    f->DecRef();
    

    wieder löscht. Was spricht denn gegen eine einheitliche Schnittstelle zur Erzeugung und Zerstörung von Instanzen, also etwas wie

    destroyFoo(f);
    

    ? (Das ist einfach meine Ansicht, ich würde das nie so machen...)



  • einheitliche Schnittstelle zur Erzeugung und Zerstörung von Instanzen

    Das ist das Symmetrieprinzip, von dem ich rede.

    Ja klar so. Wo soll das Problem sein?

    delete this halte ich fuer grundsaetzlich boese, ganz einfach weil wieder das Symmetrieprinzip verletzt ist. Ein Objekt kann sich nicht selber erzeugen, also sollte es sich auch nicht selbst zerstoeren. Sauber programmiert man anders. Auch kann das Objekt auf dem Stack liegen, was vielelicht hier nicht der Fall sein wird/kann. Auch weiss ich nicht, wie ob Code nach einem delete this (z.B. in Form von einem return) undefiniertes Verhalten erzeugt. Normalerweise ist dieses Konstrukt ueberfluessig und kann durch besseres Programmdesign entfernt werden.



  • knivil schrieb:

    delete this halte ich fuer grundsaetzlich boese, ganz einfach weil wieder das Symmetrieprinzip verletzt ist. Ein Objekt kann sich nicht selber erzeugen, also sollte es sich auch nicht selbst zerstoeren. Sauber programmiert man anders.

    Mag sein, aber ich hab mal gelesen, dass wenigstens unter win dies die einzige Möglichkeit ist, ein via LoadLibrary dynamisch geladenes Objekt (Plugin) ordentlich zu löschen, das muss einfach aus dem Speicherbereich der dll heraus geschehen. Weshalb ich mittlerweile im Plugin-Interface den Destruktor + destroy()-Methode private mache und den PluginManager als friend deklariere, welcher seinerseits eine Methode zum löschen anbietet... Damit garantiert nix falsch geht.



  • Ich kann dir nicht folgen: Die DLL/Plugin (fuer was eigentlich) or whatever kann fuer Objekte einfach create und destroy anbieten. Ich verwende also fuer Objekte der DLL die Funktionen der DLL zum Erzeugen und Vernichten. Zumindestens implementiere ich es so bei .so-Bibliotheken unter Linux.

    hab mal gelesen

    Eine etwas schwache Argumentation. Vielelicht war es einfach schlechtes Plugindesign. Und dein Beispiel ist eher speziell. Im Allgemeinen wuerde ich immer nach besseren Moeglichkeiten als delete this suchen. Hier gibt es die bestimmt.

    LoadLibrary dynamisch geladenes Objekt

    Keine Ahnung wie Windowsprogrammierung so aussieht, aber ladet man nicht Bibliotheken anstatt Objekte?



  • knivil schrieb:

    delete this halte ich fuer grundsaetzlich boese, ganz einfach weil wieder das Symmetrieprinzip verletzt ist. Ein Objekt kann sich nicht selber erzeugen, also sollte es sich auch nicht selbst zerstoeren. Sauber programmiert man anders. (...) Normalerweise ist dieses Konstrukt ueberfluessig und kann durch besseres Programmdesign entfernt werden.

    Intrusive Reference Counting ist an sich, von einem bestimmten Standpunkt aus betrachtet, unsauber. Intrusive Reference Counting vermischt Dinge die eigentlich nicht zusammengehören, nämlich die "lifetime control" und das eigentliche Objekt. Es macht die Sache aber nicht besser, die AddRef und Release Funktionen als nicht-Member zu implementieren. IMO macht das die Sache sogar noch schlimmer, denn der Zähler ist Teil des Objekts, also sollte er auch über Member-Funktionen angesprochen werden.

    Intrusive Reference Counting ist eine Optimierung, und wie die meisten Optimierungen, streng genommen nicht ganz "sauber". Es ist aber Common Practice und leicht zu verstehen. Daher verstehe ich deine "Bedenken" nicht ganz.

    Wenn du es "sauber" willst, dann verzichte ganz auf "Intrusive Reference Counting" ("counted body"), und verwende "counted handle" stattdessen.

    Auch weiss ich nicht, wie ob Code nach einem delete this (z.B. in Form von einem return) undefiniertes Verhalten erzeugt.

    Es erzeugt kein UB, solange man this danach nichtmehr dereferenziert, oder sonstwie auf Member des Objekts zugreift. Das typische "delete this; return;" ist völlig problemlos.

    p.S.: genau das, nämlich dass sich viele Leute nicht sicher sind, ob "delete this" denn "OK" ist, ist der Grund, warum viele Leute es als "böse" verdonnern. Überleg dir mal bessere Argumente, oder hör auf etwas zu verteufeln, was du nicht verstehst.


Anmelden zum Antworten