this zu shared_ptr casten



  • Hallo,

    ich schreibe grade ein Programm, was bisher alte c-pointer verwendet hat und will das mit shared_ptr etwas aufpolieren.
    Dabei habe ich aber das Problem, dass ich jetzt Zeigerargumente in std::shared_ptr umwandeln will und in einigen Funktionen this als Parameter habe.

    Vorher:

    void Object::function(std::array<param_class *> parameters);
    

    Nachher:

    void Object::function(std::array<std::shared_ptr<param_class>> parameters);
    

    Ich rufe im Programm aus der param_clas die Funktion Object::function auf. Ich weiß, dass das nicht so toller standard ist, aber das lässt sich im Augenblick nicht anders lösen.
    Im ersten fall habe ich dafür einfach

    o->function({this})
    

    aufgerufen.
    Im zweiten Fall müsste ich vorher this in einen shared_ptr casten.
    Etwa so:

    o->function({std::shared_ptr<param_class>(this)})
    

    Ich frage mich jetzt hierbei, ob nach dem Aufruf this gelöscht wird, oder ob das zu anderen Problemen kommt, da das Memory von this nicht gefreed werden kann.
    Ist es problemlos möglich this in einen shared_pointer zu casten oder sollte ich lieber die Funktion überladen, und einmal mit shared_ptr und einmal mit regulären Pointern arbeiten?

    Vielen Dank!



  • habs nur überflogen, aber: std::shared_from_this.
    {this} ist nicht nur falsch, es ist eine Absturzgarantie.

    EDIT: Falls dein Objekt nicht von Anfang an, und nicht zwingend in einem shared_ptr lebt, und das nicht irgendwie sehr wichtig ist, würde ich die Übergabe als shared_ptr an eine Funktion stark in Frage ziehen.



  • 5cript schrieb:

    habs nur überflogen, aber: std::shared_from_this.
    {this} ist nicht nur falsch, es ist eine Absturzgarantie.

    EDIT: Falls dein Objekt nicht von Anfang an, und nicht zwingend in einem shared_ptr lebt, und das nicht irgendwie sehr wichtig ist, würde ich die Übergabe als shared_ptr an eine Funktion stark in Frage ziehen.

    Naja, ich brauche auf jeden Fall Pointer, weil meine Klassen virtual functions haben, und ich die Objekte dann nicht direkt übergeben kann.
    Die einzige Alternative ist dann wohl die Funktion überladen oder die alten Pointer benutzen, oder?





  • Die Sache beim shared_ptr ist die, dass ein shared_ptr einen Referenzzähler braucht. Ein normales Objekt hat sowas nicht, also legt ein shared_ptr für jedes referenzierte Objekt einen Referenzzähler an und teilt sich ihn mit anderen shared_ptr .

    shared_ptr<T> s1( new T() );
    shared_ptr<T> s2 = s1;
    

    s1 legt im Konstruktor einen referenzzähler für das verwaltete Objekt an und s2 benutzt den gleichen Referenzzähler. Soweit alles gut.

    void f1( const shared_ptr<T>& ptr )
    {
       ...
    }
    
    void f2( T* ptr )
    {
       // ptr lebt im shard_ptr p, für den Aufruf von f1/ wird aber ein shared_ptr
       // benötigt. Wenn ptr für den Aufruf von f2 in einen neuen shared_ptr
       // verpackt wird führt das zu doppelter Speicherfreigabe => BOOM
       f1( shared_ptr<T>( ptr ) );   
    }
    
    int main()
    {
       shared_ptr<T> p( new T() );
       f2( p.get() ); 
    }
    

    Hier werden zwei shared_ptr mit jeweils eigenem Referenzzähler erzeugt. Im Destruktor geben beide den Speicher wieder frei und erzeugen damit UB. Soweit nicht mehr alles gut.

    Eine Lösung dazu ist, von enable_shared_from_this zu erben, in diesem Fall hängt der Referenzzähler nicht am shared_ptr , sondern am verwalteten Objekt.

    struct T : std::enable_shared_from_this<T>
    {
      ...
    };
    

    Alle shared_ptr , die jetzt auf ein T verwalten, benutzen den gemeinsamen Referenzzähler von T . Wieder alles gut.

    Das ist eine Lösung, wenn es wtrklich nicht anders geht. Ansonsten bin ich vollkommen bei 5cript: Man muss keine shared_ptr herumreichen. Der Besitzer weiß, dass ein Objekt in einem shared_ptr lebt, für die weitere Benutzung sollten dann Referenzen (falls Funktionsargument erforderlich ist) oder rohe Zeiger benutzt werden (falls Funktionsargument optional ist).



  • DocShoe schrieb:

    Eine Lösung dazu ist, von enable_shared_from_this zu erben, in diesem Fall hängt der Referenzzähler nicht am shared_ptr , sondern am verwalteten Objekt.

    (...)

    Alle shared_ptr , die jetzt auf ein T verwalten, benutzen den gemeinsamen Referenzzähler von T . Wieder alles gut.

    Soweit ich weiss stimmt das nicht, der Control-Block ist nach-wie-vor extra. Zumindest war das lange Zeit in der Boost-Implementierung so.
    Der Trick bei enable_shared_from_this ist dass das Objekt nen weak_ptr auf sich selbst hat, der beim Erzeugen des 1. shared_ptr auf das Objekt befüllt wird.

    Und AFAIK ist es auch nicht erlaubt über den normalen shared_ptr(T*) Ctor mehrere shared_ptr auf das selbe Objekt zu erzeugen, auch nicht wenn die Klasse von enable_shared_from_this abgeleitet ist. Sondern man muss sich die weiteren shared_ptr über obj.shared_from_this() besorgen.

    Falls ich mich irre, bitte korrigiert mich!



  • Du hast recht, Hustbär! 😮

    Ich habe mich mit dem Thema vor über 7 Jahren beschäftigt, da ist wohl nicht allzuviel hängen geblieben.



  • Mit boost::intrusive_ptr kann man meinen o.g. Vorschlag implementieren. Wenn´s denn sein muss.


Log in to reply