Schlanke Smartpointer-Implementierung?



  • Hihi. Ja, wenn DU das sagst.


  • Mod

    TyRoXx schrieb:

    Ethon schrieb:

    Wenn man möglichst effizient und schlank bleiben wollen will, würde ich einfach eine Basis für reference-counted objects schreiben:

    class reference_counted
    {
    private:
        std::atomic_uint m_ref;
    
    public:
        void inc_ref()
        {
            ++m_ref;
        }
    
        void dec_ref()
        {
            if(std::atomic_fetch_sub(&m_ref, 1) == 0)
                delete this;
        }
    };
    

    Und davon halt alle Klassen ableiten + einen smart pointer schreiben der nur inc_ref/dec_ref aufruft. Fertig.

    Wenn man das unsinnige Ableiten weglässt, hat man wieder shared_ptr .
    Außerdem hast du vergessen, den Destruktor virtuell zu machen.
    delete this ist ohnehin sehr unschön.

    Ableiten ist schon sinnvoll um Code-Bloat zu vermeiden. Allerdings ist es immer noch Aufgabe des Smartpointers und nicht des Referenzcounters, dafür zur sorgen, dass das Objekt am Ende freigegeben wird. Und dann benötigt man auch keinen virtuellen Destruktor. atomic_fetch_sub gibt übrigens den alten Wert des Counters zurück...



  • 314159265358979 schrieb:

    hustbaer schrieb:

    D.h. ein std::vector sollte auch niemals seinen Inhalt managen, denn auf diesen hat er schliesslich keinen Einfluss.
    Saudummes Argument.

    Du bist echt dumm wie Stroh. Natürlich besitzt der vector seine Elemente, aber nicht sich selbst.

    Zeig doch ein bisschen respekt.

    Worum es geht ist Intrusiv-Verhalten. Das ist in vielen Situationen ziemlich ideal. zB wenn man eine Resource eh nur per Smartpointer verwalten wird. make_shared macht ja auch nichts viel anderes als intrusiv zu sein, nur dass make_shared halt ein boeser Hack ist.



  • Shade Of Mine schrieb:

    Zeig doch ein bisschen respekt.

    Wenn er sich absichtlich dumm stellt...

    Shade Of Mine schrieb:

    Worum es geht ist Intrusiv-Verhalten. Das ist in vielen Situationen ziemlich ideal. zB wenn man eine Resource eh nur per Smartpointer verwalten wird.

    Fahre fort.

    Shade Of Mine schrieb:

    make_shared macht ja auch nichts viel anderes als intrusiv zu sein, nur dass make_shared halt ein boeser Hack ist.

    Wie bitte? Also entweder, du hast make_shared nicht verstanden, oder ich dich nicht. Erklär mal genauer, wie du das meinst.



  • 314159265358979 schrieb:

    Shade Of Mine schrieb:

    Zeig doch ein bisschen respekt.

    Wenn er sich absichtlich dumm stellt...

    Ich stell mich nicht absichtlich dumm. Dafür bist du gerade wieder dabei in den "ich weiss was ich weiss was ich weiss was ich weiss was ich weiss was!!!!" Modus zu schalten, der dir dann jedes mal das Gehirn rausschmilzt.

    Zurück zum Thema:
    Nur weil das Objekt den Reference-Count "enthält" heisst dass noch lange nicht dass es "sich selbst besitzt".



  • 314159265358979 schrieb:

    Shade Of Mine schrieb:

    Worum es geht ist Intrusiv-Verhalten. Das ist in vielen Situationen ziemlich ideal. zB wenn man eine Resource eh nur per Smartpointer verwalten wird.

    Fahre fort.

    Was gibts da noch zu erklaeren? Intrusiv-Verhalten spart allokationen und erhoeht Cache-Lokalitaet. Das ist ne gute Sache.

    Shade Of Mine schrieb:

    make_shared macht ja auch nichts viel anderes als intrusiv zu sein, nur dass make_shared halt ein boeser Hack ist.

    Wie bitte? Also entweder, du hast make_shared nicht verstanden, oder ich dich nicht. Erklär mal genauer, wie du das meinst.[/quote]

    Dirty Hack ist vielleicht uebertrieben - aber es hat seine Kosten dass nur eine Allokation statt 2 stattfindet. make_shared allokiert einen Block an Speicher und erstellt dort drin 2 Objekte. Das bedeutet, dass wir zB keinen eigenen allocator fuer unsere Klasse verwenden koennen, wir koennen auch deshalb keinen eigenen deleter verwenden. Wir koennen das Objekt auch nicht aus dem shared_ptr raus nehmen, wenn wir es nicht mehr laenger dort verwalten lassen wollen,...

    make_shared hat viele Nachteile. Natuerlich sind diese Nachteile in fast jeder Situation durch den Performance Gewinn aufgehoben - aber ganz so toll ist make_shared doch nicht.

    Eine andere Loesung das make_shared Problem zu loesen ist die Klasse direkt intrusiv zu verwenden. Das wuerde viele Probleme von make_shared loesen (und natuerlich auch selber wieder andere haben).

    Aber wir diskutieren hier nicht wirklich ob intrusive Datenstrukturen sinnvoll sind, oder?



  • Shade Of Mine schrieb:

    make_shared hat viele Nachteile. Natuerlich sind diese Nachteile in fast jeder Situation durch den Performance Gewinn aufgehoben - aber ganz so toll ist make_shared doch nicht.

    Welche vielen Nachteile sind das? Die Sache mit dem verzögertem Freigeben des Speichers im Falle von weak_ptrs könnte man wahrscheinlich als Nachteil auffassen. Aber wann spielt der denn in der Praxis überhaupt eine Rolle? Doch in fast keiner Situation, würde ich mal behaupten. Was gibt's denn noch an Nachteilen?



  • krümelkacker schrieb:

    Was gibt's denn noch an Nachteilen?

    Sagt er doch:

    Shade Of Mine schrieb:

    aber es hat seine Kosten dass nur eine Allokation statt 2 stattfindet. make_shared allokiert einen Block an Speicher und erstellt dort drin 2 Objekte. Das bedeutet, dass wir zB keinen eigenen allocator fuer unsere Klasse verwenden koennen, wir koennen auch deshalb keinen eigenen deleter verwenden. Wir koennen das Objekt auch nicht aus dem shared_ptr raus nehmen, wenn wir es nicht mehr laenger dort verwalten lassen wollen,...



  • Es gibt meines Wissens noch allocate_shared, was einen eigenen Allocator entgegen nimmt. Das "Herauslösen" aus einem shared_ptr ist so oder so nicht vorgesehen und würde ich als Hack einstufen.



  • 20.7.2.2.6 shared_ptr creation [util.smartptr.shared.create]

    template<class T, class... Args>
    shared_ptr<T> make_shared(Args&&... args);
    
    template<class T, class A, class... Args>
    shared_ptr<T> allocate_shared(const A& a, Args&&... args);
    

    Requires: The expression ::new (pv) T(std::forward<Args>(args)...), where pv has type void* and points to storage suitable to hold an object of type T, shall be well formed. A shall be an allocator (17.6.3.5). The copy constructor and destructor of A shall not throw exceptions.

    Effects: Allocates memory suitable for an object of type T and constructs an object in that memory via the placement new expression ::new (pv) T(std::forward<Args>(args)...). The template allocate_shared uses a copy of a to allocate memory. If an exception is thrown, the functions have no effect.

    Returns: A shared_ptr instance that stores and owns the address of the newly constructed object of type T.

    Postconditions: get() != 0 && use_count() == 1

    Throws: bad_alloc, or an exception thrown from A::allocate or from the constructor of T.

    Remarks: Implementations are encouraged, but not required, to perform no more than one memory allocation. [ Note: This provides efficiency equivalent to an intrusive smart pointer. — end note ]
    [Note: These functions will typically allocate more memory than sizeof(T) to allow for internal bookkeeping structures such as the reference counts. — end note ]



  • Alexandrescu, "Modern C++ Design", falls Du darus Zugriff hast. Da ist alles sehr gut erklärt, so dass Du Dir eine speichereffiziente Implementierung schreiben kannst. Ausserdem sparst Du Dir die Forenscharmützel.



  • So, mal wieder angebracht: Ich will jetzt keinerlei Beleidigungen etc. mehr lesen, diskutiert sachlich oder lasst es bleiben. Gilt nicht nur für PI, sondern für alle.



  • ... hat es sogar public gestellt:

    http://www.informit.com/articles/article.aspx?p=31529



  • pumuckl schrieb:

    Gilt nicht nur für PI, sondern für alle.

    Womit hab ich die Ausnahme, extra genannt zu werden, verdient?


Anmelden zum Antworten