2015-12-10 9 views
5

Angenommen, ich habe eine Klasse, die eine untergeordnete Klasse von enable_shared_from_this ist. Die Dokumentation dieser Basisklasse besagt, dass es einen gemeinsamen Zeiger geben sollte, der diese Klasse vor dem Aufruf von shared_from_this besitzt. Ist es sicher, die Klasse mit new zu belegen und shared_from_this aufzurufen, um das Objekt zu verwalten?Verwenden von shared_from_this() ohne verwalteten gemeinsamen Zeiger in C++ 11

Antwort

1

Nein, es ist nicht sicher. Sie sollten shared_from_this nur aufrufen, wenn das Objekt von einer shared_ptr verwaltet wird, die nicht über new zugeordnet ist (ohne eine zugehörige shared_ptr). Zum Beispiel dieser Code

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

Test *test = new Test; 
std::shared_ptr<Test> test2 = test->getptr(); 

wird werfen std::bad_weak_ptr (zumindest bei der Verwendung von libstdc++). Aber das ist OK:

std::shared_ptr<Test> test(new Test); 
std::shared_ptr<Test> test2 = test->getptr(); 
+0

Was passiert mit Test2, wenn ich nach dem Aufruf von test-> getptr() mit dem Verwalten eines neuen Zeigers mit Test beginne. Ist das ein sicherer Anwendungsfall? Ich frage das, weil boost :: asio viele dieser Konstrukte verwendet, wenn asynchrone Empfangs- oder Schreiboperationen gestartet werden. – Gustavo

+0

@Gustavo Sie können den Zeiger nach einem Aufruf von 'getptr' nicht mehr verwalten, da der letztere nur dann wirft, wenn er bereits verwaltet wird (durch einen shared_ptr). – vitaut

+0

Ich meine den folgenden Fall: Std :: shared_ptr Test (neuer Test); Std :: shared_ptr test2 = test-> getptr(); test.reset (neuer AnotherTest); /* Machen Sie etwas mit test2 */ Ist dies ein gültiger Anwendungsfall? – Gustavo

3

Aus dem Standard:

§ 20.8.2.4

Shared_ptr shared_from_this();

shared_ptr shared_from_this() const;

7 * Erfordert: enable_shared_from_this ist eine leicht zugängliche Basisklasse von T. sein so wird dadurch ein Subobjekt eines Objekts t vom Typ T sein: Es soll mindestens ein shared_ptr Beispiel p, die & t besitzt.

8 Rückgabe: Ein Shared_ptr-Objekt r, das den Besitz mit p teilt.

9 Nachbedingungen: r.get() == diese

Wenn Sie shared_from_this() innerhalb einer Klasse aufrufen, die nicht von einem shared_ptr das Ergebnis verwaltet wird undefinierten Verhalten weil Sie nicht einer von erfüllt haben die dokumentierten Voraussetzungen der Methode.

Ich weiß aus Erfahrung, dass in der [aktuellen Version von] libC++ das Ergebnis eine Ausnahme ausgelöst wird. Wie alle undefined Verhalten ist dies jedoch nicht verlässlich.

2

Die Dokumentation dieser Basisklasse besagt, dass vor dem Aufruf von shared_from_this ein gemeinsamer Zeiger vorhanden sein sollte, der dieses [Objekt] besitzt.

Okay, cool.

Ist es sicher, das [Objekt] mit new zuzuweisen und shared_from_this aufzurufen, um das Objekt zu verwalten?

Nein. Vor dem Aufruf von shared_from_this sollte ein gemeinsamer Zeiger vorhanden sein, der dieses [Objekt] besitzt.

1

Wie bereits von anderen Benutzern erwähnt, führen Aufrufe von shared_from_this an Instanzen, die nicht zu shared_ptr gehören, zu einem undefinierten Verhalten (normalerweise eine Ausnahme, aber es gibt keine Garantien).

Also, warum noch eine Antwort?

Weil ich mir die gleiche Frage einmal tat und bekam fast die gleiche Antwort, dann begann ich mit einer anderen Frage zu kämpfen, die unmittelbar danach entstanden - wie kann ich garantieren, so dass alle Instanzen von einem shared_ptr verwaltet werden?

Der Vollständigkeit halber füge ich eine weitere Antwort mit ein paar Details zu diesem Aspekt hinzu.
Hier eine einfache Lösung, die zuvor nicht erwähnt wurde.

So einfach eine Lösung, in der Tat: private Konstruktoren, Factory-Methode und variadische Vorlage s.
Es folgt ein Snippet, dass sie alle zusammen in einem minimalen Beispiel mischt:

#include<memory> 
#include<utility> 

class C: public std::enable_shared_from_this<C> { 
    C() = default; 
    C(const C &) = default; 
    C(C &&) = default; 
    C& operator=(const C &) = default; 
    C& operator=(C &&c) = default; 

public: 
    template<typename... Args> 
    static std::shared_ptr<C> create(Args&&... args) noexcept { 
     return std::shared_ptr<C>{new C{std::forward<Args>(args)...}}; 
    } 

    std::shared_ptr<C> ptr() noexcept { 
     return shared_from_this(); 
    } 
}; 

int main() { 
    std::shared_ptr<C> c1 = C::create(); 
    std::shared_ptr<C> c2 = C::create(*c1); 
    std::shared_ptr<C> c3 = c2->ptr(); 
    // these won't work anymore... 
    // C c4{}; 
    // std::shared_ptr<C> c5 = std::make_shared<C>(); 
    // std::shared_ptr<C> c6{new C{}}; 
    // C c7{*c1}; 
    // ... and so on ... 
} 

Der Grund (? Trivial) Idee ist, die explizite Konstruktion von neuen Instanzen zu verbieten, aber hier die Factory-Methode create genannt durch die Verwendung .
Variadische Vorlagen werden verwendet, um das Schreiben mehrerer Factory-Methoden zu vermeiden, nicht mehr. Perfekte Weiterleitung hilft uns, das richtig zu machen.

Ziemlich einfach, nicht wahr?
Jedenfalls brauchte ich eine Weile, um das herauszufinden, also hoffe ich, dass dies zukünftigen Lesern helfen wird, einmal über dieselben Zweifel hinweg zu kommen.

Verwandte Themen