2015-12-10 14 views
5

私は、enable_shared_from_thisの子クラスであるクラスを持っているとしましょう。この基本クラスのドキュメントでは、shared_from_thisを呼び出す前に、このクラスを所有する共有ポインタが必要であることを示しています。クラスをnewで割り当て、オブジェクトを管理するshared_from_thisを呼び出すことは安全ですか?C++でマネージド共有ポインタなしでshared_from_this()を使用する

答えて

1

いいえ、安全ではありません。オブジェクトがnew(関連付けられていないshared_ptrなし)によって割り当てられていないshared_ptrで管理されている場合は、shared_from_thisに電話をかけてください。例えば、このコード

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(); 

は(libstdc++を使用して少なくとも)std::bad_weak_ptrをスローします。しかし、これはOKです:標準から

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

test-> getptr()の呼び出しの後に、testを使って新しいポインタを管理し始めると、test2はどうなりますか?これは安全なユースケースですか? boost :: asioは、非同期の受信操作または書き込み操作を開始するときにこれらの構文を多く使用するため、これを求めています。 – Gustavo

+0

@Gustavo 'getptr'の呼び出し後にポインタを管理することはできません。なぜなら、(shared_ptrによって)すでに管理されていなければ、ポインタを投げるからです。 – vitaut

+0

私は次の場合を意味します: std :: shared_ptr test(new Test); std :: shared_ptr test2 = test-> getptr(); test.reset(new AnotherTest); /* test2で何かする*/ これは有効なユースケースですか? – Gustavo

3

§20.8.2.4

のshared_ptr shared_from_this();

shared_ptr shared_from_this()const;

7 *が必要です:enable_shared_from_thisが、これは型Tのオブジェクトtの サブオブジェクトが&トンを所有している少なくとも一つのshared_ptrのインスタンスPがなければならないものでなければならないT. のアクセス可能基底クラスでなければなりません。

8戻り値:pと所有権を共有するshared_ptrオブジェクトr。

9事後条件:r.get()==この

あなたはshared_ptrによって管理されていないクラス内shared_from_this()を呼び出す場合は、のいずれかを満たしていないため、結果は未定義の動作になります文書化された方法の前提条件

私は経験から、libC++の[現在のバージョン]では例外がスローされていることを知っています。ただし、すべて同じように未定義の動作これは信頼されるべきではありません。

2

この基本クラスのドキュメントには、shared_from_thisを呼び出す前にこの[オブジェクト]を所有する共有ポインタが必要であると記載されています。

大丈夫です。

[オブジェクト]をnewで割り当てても安全ですか?オブジェクトを管理するにはshared_from_thisを呼び出しますか?

いいえshared_from_thisを呼び出す前に、この[オブジェクト]を所有する共有ポインタが必要です。

1

shared_ptrが所有していないインスタンスのshared_from_thisへの呼び出しは、未定義の動作(通常は例外ですが、保証はありません)になります。

なぜ、もう一度答えますか?

私は一度自分自身に同じ質問をしたとほぼ同じ答えを得たので、私はその直後に発生した別の質問に苦しんで開始 - は、どのように私はすべてのインスタンスがshared_ptrによって管理されているので、保証することができますか?

完全性のために、私はこの側面に関するいくつかの詳細と別の答えを追加します。
これまで説明していなかった単純な解決策です。

だから、シンプルなソリューション、実際:プライベートコンストラクタファクトリメソッド可変長テンプレート秒。
それは、最小限の例で一緒にそれらのすべてをミックススニペットを次に示します。

#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 ... 
} 

基本的な(?些細な)アイデアは、新しいインスタンスの明示的な建設を禁止することであるが、ここで呼ばれるファクトリメソッドを使用してcreate
バリアントテンプレートは、いくつかのファクトリメソッドの作成を避けるために使用されます。パーフェクトフォワーディングは、正しい方法でこれを行うのに役立ちます。

かなりシンプルですね。
とにかくそれを理解するのはしばらく時間がかかりました。だから、これは将来の読者が同じ疑念の中で一度助けてくれることを願っています。

関連する問題