web-dev-qa-db-de.com

Was ist der Nutzen von enable_shared_from_this?

Ich lief über enable_shared_from_this Beim Lesen der Boost.Asio-Beispiele und nach dem Lesen der Dokumentation bin ich immer noch verloren, wie dies richtig verwendet werden sollte. Kann mir jemand bitte ein Beispiel und/oder eine Erklärung geben, wann die Verwendung dieser Klasse sinnvoll ist?.

327
fido

Es ermöglicht Ihnen, eine gültige shared_ptr - Instanz auf this zu setzen, wenn Sie nur this haben. Ohne diesen Code hätten Sie keine Möglichkeit, einen shared_ptr Für this zu erhalten, es sei denn, Sie hatten bereits einen als Mitglied. Dieses Beispiel aus der Boost-Dokumentation für enable_shared_from_this :

class Y: public 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
}

Die Methode f() gibt ein gültiges shared_ptr Zurück, obwohl sie keine Mitgliedsinstanz hatte. Beachten Sie, dass Sie dies nicht einfach tun können:

class Y: public enable_shared_from_this<Y>
{
public:

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

Der gemeinsam genutzte Zeiger, den dieser zurückgegeben hat, hat einen anderen Referenzzähler als der "richtige", und einer von ihnen verliert und hält einen baumelnden Verweis, wenn das Objekt gelöscht wird.

enable_shared_from_this Ist Teil des C++ 11-Standards geworden. Sie können es auch von dort sowie von Boost erhalten.

339

aus dem Artikel von Dr. Dobbs über schwache Zeiger geht hervor, dass dieses Beispiel leichter zu verstehen ist (Quelle: http://drdobbs.com/cpp/184402026 ):

... Code wie dieser funktioniert nicht richtig:

int *ip = new int;
shared_ptr<int> sp1(ip);
shared_ptr<int> sp2(ip);

Keines der beiden Objekte shared_ptr Weiß etwas über das andere, daher versuchen beide, die Ressource freizugeben, wenn sie zerstört werden. Das führt normalerweise zu Problemen.

Wenn eine Mitgliedsfunktion ein shared_ptr - Objekt benötigt, das das Objekt besitzt, für das sie aufgerufen wird, kann sie nicht einfach ein Objekt im laufenden Betrieb erstellen:

struct S
{
  shared_ptr<S> dangerous()
  {
     return shared_ptr<S>(this);   // don't do this!
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->dangerous();
   return 0;
}

Dieser Code hat das gleiche Problem wie das vorherige Beispiel, allerdings in einer subtileren Form. Bei seiner Erstellung besitzt das Objekt shared_pt R sp1 Die neu zugewiesene Ressource. Der Code in der Member-Funktion S::dangerous Kennt dieses shared_ptr - Objekt nicht. Daher unterscheidet sich das zurückgegebene shared_ptr - Objekt von sp1. Das Kopieren des neuen shared_ptr - Objekts nach sp2 Hilft nicht. Wenn sp2 den Gültigkeitsbereich verlässt, wird die Ressource freigegeben, und wenn sp1 den Gültigkeitsbereich verlässt, wird die Ressource erneut freigegeben.

Um dieses Problem zu vermeiden, verwenden Sie die Klassenvorlage enable_shared_from_this. Die Vorlage akzeptiert ein Vorlagentypargument, nämlich den Namen der Klasse, die die verwaltete Ressource definiert. Diese Klasse muss wiederum öffentlich aus der Vorlage abgeleitet werden; so was:

struct S : enable_shared_from_this<S>
{
  shared_ptr<S> not_dangerous()
  {
    return shared_from_this();
  }
};

int main()
{
   shared_ptr<S> sp1(new S);
   shared_ptr<S> sp2 = sp1->not_dangerous();
   return 0;
}

Beachten Sie dabei, dass sich das Objekt, für das Sie shared_from_this Aufrufen, im Besitz eines shared_ptr - Objekts befinden muss. Das geht nicht:

int main()
{
   S *p = new S;
   shared_ptr<S> sp2 = p->not_dangerous();     // don't do this
}
183

Hier ist meine Erklärung aus der Perspektive von Schrauben und Muttern (die Antwort oben hat bei mir nicht "geklickt"). * Beachten Sie, dass dies das Ergebnis der Untersuchung der Quelle für shared_ptr und enable_shared_from_this ist, die in Visual Studio 2012 enthalten sind. Vielleicht implementieren andere Compiler enable_shared_from_this anders ... *

enable_shared_from_this<T> fügt weak_ptr<T> eine private Instanz zu T hinzu, die den Wert 'eine wahre Referenzzahl' für die Instanz von T enthält.

Wenn Sie also zum ersten Mal einen shared_ptr<T> auf einem neuen T * erstellen, wird der interne schwache_ptr dieses T * mit einer Nachzählung von 1 initialisiert. Der neue shared_ptr wird im Grunde genommen auf diesen weak_ptr zurückgesetzt.

T kann dann in seinen Methoden shared_from_this aufrufen, um eine Instanz von shared_ptr<T> abzurufen, die auf denselben intern gespeicherten Referenzzähler zurückgreift. Auf diese Weise haben Sie immer einen Ort, an dem die Referenzanzahl von T* gespeichert wird, anstatt mehrere shared_ptr Instanzen zu haben, die nichts voneinander wissen, und jeder denkt, dass sie der shared_ptr sind ist verantwortlich für die Nachzählung von T und deren Löschung, wenn deren Nachzählung Null erreicht.

27
mackenir

Beachten Sie, dass die Verwendung von boost :: intrusive_ptr nicht unter diesem Problem leidet. Dies ist oft eine bequemere Möglichkeit, um dieses Problem zu umgehen.

3
blais

In c ++ 11 und höher ist es genau dasselbe: Es soll die Fähigkeit ermöglichen, this als gemeinsamen Zeiger zurückzugeben, da this Ihnen einen rohen Zeiger gibt.

mit anderen Worten: Sie können Code wie diesen umwandeln

class Node {
public:
    Node* getParent const() {
        if (m_parent) {
            return m_parent;
        } else {
            return this;
        }
    }

private:

    Node * m_parent = nullptr;
};           

das mögen:

class Node : std::enable_shared_from_this<Node> {
public:
    std::shared_ptr<Node> getParent const() {
        std::shared_ptr<Node> parent = m_parent.lock();
        if (parent) {
            return parent;
        } else {
            return shared_from_this();
        }
    }

private:

    std::weak_ptr<Node> m_parent;
};           
2
mchiasson