Polymorpher Smartpointer



  • 314159265358979 schrieb:

    Da kann ein unique_ptr, shared_ptr, weak_ptr, some_other_ptr, und theoretisch auch ein roher, nicht besitzender Zeiger drin sein.

    Was ich mit meinem zugegeben etwas kurzen Post sagen wollte: unique_ptr ist doch so ein polymorpher Smartpointer. Oder andersrum: was an std::unique_ptr genügt den Ansprüchen nicht, die du geschildert hast? (oder shared_ptr)

    struct Base 
    { virtual ~Base(){} }
    
    struct Derived : Base
    {}
    
    int main()
    {
      std::unique_ptr<Base> basePretender(new Derived());
    }
    


  • Ich glaub er will nicht einen Smartptr, der mit polymorphen Objekten umgehen kann, sondern einen Smartptr, der so funktioniert, dass z.B. die Klasse, die den Smartptr als Attribut hält, selbst nicht weiß, was für einen Besitzesgrad ggü. dem verwiesenen Objekt der Smartptr hat.

    Aber mich würde wirklich gerne Mal ein Szenario interessieren, in dem das hilfreich ist.



  • Wenn du dich um nichts kuemmern moechtest, dir alles egal ist, du keine Ahnung hast, dann nimm Garbage Collection!



  • knivil schrieb:

    Wenn du dich um nichts kuemmern moechtest, dir alles egal ist, du keine Ahnung hast, dann nimm Garbage Collection!

    So nen Kommentar kannst du dir schenken. Mein Post ist durchaus ernst gemeint.

    Es geht darum, dass es (wenn wir mal von Funktionen sprechen) der Funktion vollkommen egal ist, wie das Objekt verwaltet wird. Das einzig wichtige ist, dass sie eines bekommt.



  • Mein Beitrag ist auch ernst gemeint. SmartPointer erleichtern das Verwalten der Ownership und vereinfachen das Bookkeeping und helfen Fehler zu vermeiden. D.h. Du musst dir trotzdem Gedanken ueber Ownership machen. Das nimmt dir der SmartPointer nicht ab.

    Es geht darum, dass es (wenn wir mal von Funktionen sprechen) der Funktion vollkommen egal ist, wie das Objekt verwaltet wird. Das einzig wichtige ist, dass sie eines bekommt.

    Dann schau dir den Vortrag von Herb Sutter bei Going Native 2012 an: Benutze einfach normale Zeiger oder Referenzen, wenn die Funktion der Lebenszeit des Objektes nichts hinzufuegt.



  • Dangling Pointer ftw. (Vortrag habe ich übrigens gesehen.)



  • 314159265358979 schrieb:

    Dangling Pointer ftw.

    Schreib doch einfach mal ein Codebeispiel in dem du das Problem das du siehst zeigst.



  • Sobald du mehrere Threads hast, kann sowas leicht passieren, wenn man nicht höllisch aufpasst. Ich schreib hier jetzt sicher keine multi-threaded Anwendung.



  • 314159265358979 schrieb:

    Dangling Pointer ftw.

    Wie stellst du dir das vor? Der Pointer soll nichts über den Besitz des Objektes wissen (somit natürlich nicht in die Besitzverhältnisse irgendwie eingreifen), aber trotzdem magisch verhindern, dass es zu früh freigegeben wird?



  • wenn wir mal von Funktionen sprechen ... Dangling Pointer ftw.

    Das widerspricht sich. Wenn die Funktion einen Pointer/Referenz entgegennimmt, dass Objekt von aussen nicht zerstoert wird (Funktion ist kuerzer als die Lebenszeit des Objektes), wie kann es dann zu Dangling Pointers kommen?

    Ansonsten bleibe ich dabei: Du willst keinen SmartPointer sondern Garbage Collection.

    Sobald du mehrere Threads hast

    Das erwaehnst du jetzt das erste mal. D,h. die Lebenszeit des Objektes ist nicht genau festgelegt, d.h. eine Funktion in einem anderen Thread kann die Lebenszeit verlaengern, d.h. du musst dir ueber Ownership gedanken machen, zumindest ueber die Lebenszeit. Da mehrere Threads ein Objekte benutzen waere shared_ptr angemessen.



  • Der Pointer darf ja was darüber wissen, nur soll dieses Wissen für den Funktionsaufruf weggekapselt sein...

    knivil: Ok, so... ja, so versteh ich das.



  • 314159265358979 schrieb:

    Da kann ein unique_ptr, shared_ptr, weak_ptr, some_other_ptr, und theoretisch auch ein roher, nicht besitzender Zeiger drin sein.

    Wenn du mich fragst, machst du grad irgendwas falsch wenn du sowas haben willst. So ein Zeiger macht keinen Sinn. Die oben genannten Smart Pointer haben kein gemeinsames Konzept, sie von einer gemeinsamen Basis abzuleiten wär also schon rein logisch falsch.
    Ich glaub was du suchst ist einfach nur ein roher Zeiger.



  • dot schrieb:

    Die oben genannten Smart Pointer haben kein gemeinsames Konzept

    Doch, schon. Alle vermitteln irgendeine Art von Besitz, und bei keinem muss man sich um die Freigabe kümmern.

    Trotzdem, ich halte es für sinnvoller sich über den Scope seines Objekts Gedanken zu machen. Was Threads angeht (wo auch immer die plötzlich herkommen oO) würde ich gerne mal ein Beispiel sehen, bei dem das große Vorteile gegenüber shared_ptr hat.

    314159265358979 schrieb:

    Ich schreib hier jetzt sicher keine multi-threaded Anwendung.

    Ja nun, irgendeinen Fall solltest du doch im Kopf haben?



  • cooky451 schrieb:

    dot schrieb:

    Die oben genannten Smart Pointer haben kein gemeinsames Konzept

    Doch, schon. Alle vermitteln irgendeine Art von Besitz, und bei keinem muss man sich um die Freigabe kümmern.

    Nein. Ein weak_ptr z.B. besitzt nichts.



  • cooky451 schrieb:

    dot schrieb:

    Die oben genannten Smart Pointer haben kein gemeinsames Konzept

    Doch, schon. Alle vermitteln irgendeine Art von Besitz, und bei keinem muss man sich um die Freigabe kümmern.

    Nein, weak_ptr vermittelt keinen Besitz sondern ist nur ein Verweis auf etwas, was vielleicht noch von einem shared_ptr gehalten wird.



  • Ich implementiers einfach mal, vlt wird dann klarer, wie das Ding funktioniert.

    template <typename T>
    struct unknown_ptr
    {
        struct ptr_wrapper_base
        {
            virtual T&       operator * ();
            virtual T const& operator * () const;
    
            virtual T*       operator -> ();
            virtual T const* operator -> () const;
    
            virtual ~ptr_wrapper_base() {}
        };
    
        template <typename Ptr>
        struct ptr_wrapper : ptr_wrapper_base
        {
            Ptr ptr;
    
            ptr_wrapper(Ptr ptr)
                ptr(std::move(ptr))
            {}
    
            virtual T&       operator * ()       { return *ptr; }
            virtual T const& operator * () const { return *ptr; }
    
            virtual T*       operator -> ()       { return &*ptr; }
            virtual T const* operator -> () const { return &*ptr; }
        };
    
        T&       operator * ()       { return **ptr; }
        T const& operator * () const { return **ptr; }
    
        T*       operator -> ()       { return &**ptr; }
        T const* operator -> () const { return &**ptr; }
    
        template <typename Ptr>
        unknown_ptr(Ptr ptr)
            : ptr(new ptr_wrapper<Ptr>(std::move(ptr)))
        {}
    
    private:
        std::unique_ptr<ptr_wrapper_base> ptr;
    };
    


  • Und ist klar was du machen willst. Und ist aber nicht klar warum.



  • Ich will ein Objekt an eine Funktion übergeben. Wo dieses Objekt herkommt, ist egal. Wichtig ist, dass das Objekt ggf. am Leben gehalten werden muss. Deshalb kann ich keinen rohen Zeiger/keine Referenz verwenden.

    Ein konkretes Beispiel hab ich im Moment leider nicht, ist schon ein paar Tage her, dass ich sowas wollte und ich habs mittlerweile vergessen.



  • Wie sieht's mit shared_ptr mit Deleter aus?



  • @314159265358979
    shared_ptr erfüllt deine Vorgaben, bis auf zwei: er ist zusätzlich noch kopierbar, und er kann nie "weak" sein.
    Die Kopierbarkeit kannst du leicht entfernen, indem du ihn in eine eigene Klasse einwickelst.

    Dann brauchst du noch drei verschiedene Konstruktoren: 1x für nicht besitzend, 1x für shared und 1x für unique.
    Nicht besitzend initialisiert den internen shared_ptr mit einem Dummy-Deleter, shared kopiert einfach den übergebenen shared_ptr und unique erzeugt einen neuen shared_ptr, der ausserhalb des Wrapper-Objekts nicht zugänglich ist, d.h. auch nicht kopiert werden kann = unique ownership.

    Wenn das Ding unbedingt auch "weak" sein können soll, dann könntest du nen variant<shared_ptr, weak_ptr> in der Wrapper-Klasse verwenden.

    Dafür müssen sich dann alle User der Klasse damit rumschlagen den Fall "Objekt ist weg" irgendwie behandeln zu müssen.

    Das Warum ist mir aber auch nicht klar, ich hatte für sowas noch keine Anwendung.

    Einzig bei Strings hätte ich mir was ähnliches mal gewünscht: eine lightweight "String-Referenz" Klasse die nicht besitzend sein muss (aber kann). Und zwar für (fast) ausschliesslich eine Anwendung, nämlich als Funktions-Parameter. Damit könnte man Funktionen machen die folgende Dinge können:
    * Ohne strlen() die Grösse ermitteln, daher auch...
    * ...Strings mit eingebetteten Null-Charactern verarbeiten
    * Ohne .c_str() mit std::(w)string kompatibel sein (über passenden Conversion-Ctor)
    * Ggf. den String "klauen" (move) können, wenn der "String-Ref" Parameter besitzend ist, bzw. wenn nicht einfach eine Kopie machen

    Und das alles mit minimalem Overhead (zumindest minimal im Vergleich zu "einfach std::string verwenden", wo dann jedesmal aus einem Literal ein std::string Parameter erzeugt werden muss).

    Zusätzlich würde die Klasse natürlich alle Typedefs und const-Funktionen von std::string anbieten.


Anmelden zum Antworten