Boost :: shared_ptr dafür bekommen

76

Ich benutze boost:shared_ptrin meinem Code ausgiebig . Tatsächlich werden die meisten Objekte, die auf dem Heap zugeordnet sind, von a gehalten shared_ptr. Leider bedeutet dies, dass ich nicht thisin eine Funktion übergehen kann, die eine übernimmt shared_ptr. Betrachten Sie diesen Code:

void bar(boost::shared_ptr<Foo> pFoo)
{
    ...
}

void Foo::someFunction()
{
    bar(this);
}

Hier gibt es zwei Probleme. Erstens wird dies nicht kompiliert, da der T * -Konstruktor für shared_ptrexplizit ist. Zweitens, wenn ich erzwinge, dass es mit erstellt bar(boost::shared_ptr<Foo>(this))wird, habe ich einen zweiten gemeinsamen Zeiger auf mein Objekt erstellt, der schließlich zu einem doppelten Löschen führt.

Dies bringt mich zu meiner Frage: Gibt es ein Standardmuster zum Abrufen einer Kopie des vorhandenen gemeinsam genutzten Zeigers, von dem Sie wissen, dass er in einer Methode für eines dieser Objekte vorhanden ist? Ist die Verwendung von aufdringlichen Referenzzählungen hier meine einzige Option?

Joe Ludwig
quelle
" Zählt hier die Verwendung von aufdringlichen Referenzen? " Was ist mit dieser Option falsch?
Neugieriger
Vielleicht nichts. Kommt auf deine Umstände an. Dadurch werden Ihre Objekte größer und funktionieren möglicherweise nicht an Orten, an denen Sie keine Kontrolle über die Klassen haben, für die Sie Smartpointers verwenden.
Joe Ludwig
enabe_shared_from_this ist jetzt in std::. Schau dir meine Antwort an.
Johan Lundberg

Antworten:

102

Sie können von enable_shared_from_this ableiten und dann "shared_from_this ()" anstelle von "this" verwenden, um einen gemeinsamen Zeiger auf Ihr eigenes Selbstobjekt zu erzeugen.

Beispiel im Link:

#include <boost/enable_shared_from_this.hpp>

class Y: public boost::enable_shared_from_this<Y>
{
public:

    shared_ptr<Y> f()
    {
        return shared_from_this();
    }
}

int main()
{
    shared_ptr<Y> p(new Y);
    shared_ptr<Y> q = p->f();
    assert(p == q);
    assert(!(p < q || q < p)); // p and q must share ownership
}

Es ist eine gute Idee, wenn Sie Threads aus einer Mitgliedsfunktion erzeugen, stattdessen :: bind an ein shared_from_this () zu erhöhen. Dadurch wird sichergestellt, dass das Objekt nicht freigegeben wird.

Brian R. Bondy
quelle
f () ist wie ".Copy ()" und es ist auch eine flache Kopie.
Anton Andreev
19

Verwenden Sie einfach einen Rohzeiger für Ihren Funktionsparameter anstelle von shared_ptr. Der Zweck eines intelligenten Zeigers besteht darin, die Lebensdauer des Objekts zu steuern. Die Lebensdauer des Objekts wird jedoch bereits durch C ++ - Gültigkeitsregeln garantiert: Sie bleibt mindestens so lange bestehen wie das Ende Ihrer Funktion. Das heißt, der aufrufende Code kann das Objekt möglicherweise nicht löschen, bevor Ihre Funktion zurückkehrt. Somit ist die Sicherheit eines "dummen" Zeigers gewährleistet, solange Sie nicht versuchen, das Objekt in Ihrer Funktion zu löschen.

Sie müssen ein shared_ptr nur dann an eine Funktion übergeben, wenn Sie den Besitz des Objekts an die Funktion übergeben oder die Funktion eine Kopie des Zeigers erstellen möchten.

Mark Ransom
quelle
1
Einverstanden. Oft können Sie foo (const Object * object_ptr) {} foo (obj.get ()) verwenden; Dabei ist obj ein boost :: shared_ptr <Objekt>. Suchen Sie im Internet nach Herb Sutter, einem anderen und Artikelschreiber, der einige großartige Informationen zu diesem und ähnlichen Themen hat.
Mrd.
1
Es ist wenig nebensächlich, aber ... Wenn Sie einen Zeiger verwenden können, können Sie (höchstwahrscheinlich) eine Referenz verwenden, was IMO besser ist.
Denis-Bu
1
@ denis-bu, außer wenn ein NULLZeiger möglich ist. Aber du machst einen guten Punkt.
Mark Ransom
Offensichtlich 12 Jahre zu spät, aber ich verstehe diese Antwort nicht. Der Typ des zu barenden Arguments unterscheidet sich von einem Rohzeiger. Ohne das Schlüsselwort "explizit", das dazu führen würde, dass das Übergeben von "this" nicht kompiliert werden kann, würde der Compiler nicht versuchen, den shared_ptr aus dem Rohzeiger zu erstellen, indem er den Referenzzählblock im Prozess erstellt, den Referenzzähler auf 1 setzt und dann weiter Verlassen Sie den Balken, verringern Sie den Referenzzähler auf 0 und versuchen Sie, "dies" zu löschen.
Foster Boondoggle
1
@FosterBoondoggle Mein Vorschlag war, den Parametertyp von barin einen Rohzeiger zu ändern .
Mark Ransom
9

Erstellen Sie wirklich mehr freigegebene Kopien von pFoo in der Leiste? Wenn Sie innen nichts Verrücktes tun, tun Sie einfach Folgendes:


void bar(Foo &foo)
{
    // ...
}
Greg Rogers
quelle
5

Mit C ++ 11 shared_ptrund enable_shared_from_thisist jetzt in der Standardbibliothek. Letzteres ist, wie der Name schon sagt, genau für diesen Fall.

http://en.cppreference.com/w/cpp/memory/shared_ptr

http://en.cppreference.com/w/cpp/memory/enable_shared_from_this

Das Beispiel basiert auf den obigen Links:

struct Good: std::enable_shared_from_this<Good>{
    std::shared_ptr<Good> getptr() {
        return shared_from_this();
    }
};

verwenden:

std::shared_ptr<Good> gp1(new Good);
std::shared_ptr<Good> gp2 = gp1->getptr();
std::cout << "gp2.use_count() = " << gp2.use_count() << '\n';
Johan Lundberg
quelle
3

Die Funktion, die einen Zeiger akzeptiert, möchte eines von zwei Verhaltensweisen ausführen:

  • Besitzen Sie das übergebene Objekt und löschen Sie es, wenn es den Gültigkeitsbereich verlässt. In diesem Fall können Sie einfach X * akzeptieren und sofort ein scoped_ptr um dieses Objekt (im Funktionskörper) wickeln. Dies funktioniert, um "dies" oder im Allgemeinen jedes Heap-zugewiesene Objekt zu akzeptieren.
  • Geben Sie einen Zeiger (den Sie nicht besitzen) für das übergebene Objekt frei. In diesem Fall möchten Sie überhaupt kein scoped_ptr verwenden, da Sie das Objekt am Ende Ihrer Funktion nicht löschen möchten. In diesem Fall möchten Sie theoretisch einen shared_ptr (ich habe gesehen, dass er an anderer Stelle als linked_ptr bezeichnet wird). Die Boost-Bibliothek enthält eine Version von shared_ptr . Dies wird auch in Scott Meyers 'Effective C ++ - Buch (Punkt 18 in der 3. Ausgabe) empfohlen.

Bearbeiten: Hoppla, ich habe die Frage leicht falsch verstanden und sehe jetzt, dass diese Antwort die Frage nicht genau anspricht. Ich werde es trotzdem weglassen, falls dies für jemanden hilfreich sein könnte, der an ähnlichem Code arbeitet.

Tyler
quelle