49 Stimmen

Was sind mögliche Gefahren bei der Verwendung von boost::shared_ptr?

Welche Möglichkeiten gibt es, sich bei der Verwendung von boost::shared_ptr ? Mit anderen Worten, welche Fallstricke muss ich vermeiden, wenn ich die boost::shared_ptr ?

43voto

Kaz Dragon Punkte 6406

Zyklische Referenzen: a shared_ptr<> zu etwas, das einen shared_ptr<> zum ursprünglichen Objekt. Sie können verwenden weak_ptr<> um diesen Kreislauf zu durchbrechen, natürlich.


Ich füge das Folgende als Beispiel für das, was ich in den Kommentaren meine, hinzu.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        if (parent_) parent_->frob();
    }

private :
    void do_frob();
    shared_ptr<node> parent_;
    vector< shared_ptr<node> > children_;
};

In diesem Beispiel haben Sie einen Baum von Knoten, von denen jeder einen Zeiger auf seine Eltern enthält. Die frob()-Mitgliedsfunktion, aus welchem Grund auch immer, kräuselt sich nach oben durch den Baum. (Dies ist nicht völlig abwegig; einige GUI-Frameworks arbeiten auf diese Weise).

Das Problem ist, dass, wenn Sie den Bezug zum obersten Knoten verlieren, der oberste Knoten immer noch starke Bezüge zu seinen Kindern hat, und alle seine Kinder haben auch einen starken Bezug zu ihren Eltern. Dies bedeutet, dass es zirkuläre Referenzen gibt, die alle Instanzen davon abhalten, sich selbst zu bereinigen, während es keine Möglichkeit gibt, den Baum vom Code aus zu erreichen, da dieser Speicher leckt.

class node : public enable_shared_from_this<node> {
public :
    void set_parent(shared_ptr<node> parent) { parent_ = parent; }
    void add_child(shared_ptr<node> child) {
        children_.push_back(child);
        child->set_parent(shared_from_this());
    }

    void frob() {
        do_frob();
        shared_ptr<node> parent = parent_.lock(); // Note: parent_.lock()
        if (parent) parent->frob();
    }

private :
    void do_frob();
    weak_ptr<node> parent_; // Note: now a weak_ptr<>
    vector< shared_ptr<node> > children_;
};

Hier ist der übergeordnete Knoten durch einen schwachen Zeiger ersetzt worden. Er hat keinen Einfluss mehr auf die Lebensdauer des Knotens, auf den er verweist. Wenn also der oberste Knoten, wie im vorigen Beispiel, aus dem Geltungsbereich verschwindet, hat er zwar starke Referenzen auf seine Kinder, aber seine Kinder haben keine starken Referenzen auf ihre Eltern. Es gibt also keine starken Verweise auf das Objekt, und es bereinigt sich selbst. Dies wiederum führt dazu, dass die Kinder ihre eine starke Referenz verlieren, was wiederum dazu führt, dass sie sich selbst bereinigen, und so weiter. Kurz gesagt, das wird kein Leck sein. Und nur durch strategisches Ersetzen einer shared_ptr<> mit einer weak_ptr<>.

Anmerkung: Das oben Gesagte gilt gleichermaßen für std::shared_ptr<> und std::weak_ptr<> wie für boost::shared_ptr<> und boost::weak_ptr<>.

26voto

Michael Burr Punkte 320591

Erstellen mehrerer unverbundener shared_ptr zu demselben Objekt:

#include <stdio.h>
#include "boost/shared_ptr.hpp"

class foo
{
public:
    foo() { printf( "foo()\n"); }

    ~foo() { printf( "~foo()\n"); }
};

typedef boost::shared_ptr<foo> pFoo_t;

void doSomething( pFoo_t p)
{
    printf( "doing something...\n");
}

void doSomethingElse( pFoo_t p)
{
    printf( "doing something else...\n");
}

int main() {
    foo* pFoo = new foo;

    doSomething( pFoo_t( pFoo));
    doSomethingElse( pFoo_t( pFoo));

    return 0;
}

18voto

Brian Campbell Punkte 304982

Konstruktion eines anonymen temporären gemeinsamen Zeigers, zum Beispiel innerhalb der Argumente eines Funktionsaufrufs:

f(shared_ptr<Foo>(new Foo()), g());

Denn es ist zulässig, dass die new Foo() ausgeführt werden soll, dann g() angerufen, und g() eine Ausnahme auslösen, ohne dass die shared_ptr jemals eingerichtet werden, so dass die shared_ptr nicht die Möglichkeit hat, die Foo Objekt.

13voto

Seien Sie vorsichtig bei der Erstellung von zwei Zeigern auf dasselbe Objekt.

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d( b.get() );
} // d goes out of scope here, deletes pointer

b->doSomething(); // crashes

verwenden Sie stattdessen diese

boost::shared_ptr<Base> b( new Derived() );
{
  boost::shared_ptr<Derived> d = 
    boost::dynamic_pointer_cast<Derived,Base>( b );
} // d goes out of scope here, refcount--

b->doSomething(); // no crash

Außerdem sollten alle Klassen, die shared_ptrs enthalten, Kopierkonstruktoren und Zuweisungsoperatoren definieren.

Versuchen Sie nicht, shared_from_this() im Konstruktor zu verwenden - es wird nicht funktionieren. Erstellen Sie stattdessen eine statische Methode, um die Klasse zu erstellen, und lassen Sie diese eine shared_ptr zurückgeben.

Ich habe Referenzen auf shared_ptrs ohne Probleme übergeben. Stellen Sie einfach sicher, dass es kopiert wird, bevor es gespeichert wird (d.h. keine Referenzen als Klassenmitglieder).

12voto

Frank Punkte 60769

Hier sind zwei Dinge, die Sie vermeiden sollten:

  • Aufrufen der get() Funktion, um den Rohzeiger zu erhalten und ihn zu verwenden, nachdem das Objekt, auf das gezeigt wird, den Gültigkeitsbereich verlassen hat.

  • Die Übergabe eines Verweises oder eines rohen Zeigers auf eine shared_ptr sollte ebenfalls gefährlich sein, da es die interne Zählung, die das Objekt am Leben erhält, nicht erhöht.

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X