2009-03-18 12 views
22

私は単純な基底と派生したクラスを持っていますが、両方とも持っているのはshared_from_this()です。親と派生の両方を有効にする方法

このシンプルなソリューション:

class bar2 : public foo { 
    void bar2_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
    shared_ptr<bar2> shared_from_this() 
    { 
     return boost::static_pointer_cast<bar2>(foo::shared_from_this()); 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&bar2::bar2_do_it,shared_from_this()); 
    } 
}; 

そして今、それを:私は解決策次を発見した "グーグル" の後にそう

shared_ptr<foo> ptr(shared_ptr<foo>(new bar1)); 
function<void()> f=ptr->get_callback(); 
f(); 

class foo : public enable_shared_from_this<foo> { 
    void foo_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&foo::foo_do_it,shared_from_this()); 
    } 
    virtual ~foo() {}; 
}; 

class bar1 : public foo , public enable_shared_from_this<bar1> { 
    using enable_shared_from_this<bar1>::shared_from_this; 
    void bar1_do_it() 
    { 
     cout<<"foo::do_it\n"; 
    } 
public: 
    virtual function<void()> get_callback() 
    { 
     return boost::bind(&bar1::bar1_do_it,shared_from_this()); 
    } 
}; 

は、次のコードで例外tr1::bad_weak_ptr原因働く

enable_shared_from_thisには、親と子の両方にとってより便利で正しい方法がありますか?

ありがとうございました

答えて

10

申し訳ありませんが、ありません。

問題は、shared_ptr<foo>shared_ptr<bar1>が異なるタイプです。私はフードの中で起こっていることすべてを理解していませんがコンストラクタが返され、shared_ptr<foo>に割り当てられたとき、weak_ptr<bar1>内部は何も指していないことがわかります(shared_ptr<bar1>だけがカウンタをインクリメントするため)それ自身をリセットする。 bar1::shared_from_thisget_callbackに呼び出すと、内部のweak_ptrが何も指していないため例外が発生します。

本質的に、enable_shared_from_thisは、階層内の単一のクラスから透過的にしか動作していないようです。 implementing it manuallyを試してみると、問題が明らかになるはずです。

+2

私はそれを手動で実行する試みたが、それは実際には難しいことではありません。 'My :: SmartPointer 'では、Tが 'My :: enableSmartFromThis'から派生しているかどうかをチェックします。もしそうなら、ヒープ上に参照カウンターを割り当てず、 'My :: enableSmartFromThis'のメンバーを使用してください。これで 'My :: GetSmartFromThis(this)'は簡単になり、 'this ::'を 'My :: enableSmartFromThis *'に変換して既存の参照カウントを見つけることができます。 Derivedのみが 'My :: enableSmartFromThis'から派生したときにBaseからDerivedに変換しようとしているかどうかを確認することもできます。 – MSalters

22

OPソリューションは、基本クラスで次のように定義するとより便利になります。派生クラスで定型を低減@evoskuilへ

protected: 
    template <typename Derived> 
    std::shared_ptr<Derived> shared_from_base() 
    { 
     return std::static_pointer_cast<Derived>(shared_from_this()); 
    } 
+1

かなりエレガントです。 +1 – sehe

2

同様のソリューションは、あなたはshared_from_this()機能を実装したいはずです。

EDIT:クリーナー溶液です。クラス外の関数を使用しています。これはまた、より多くのオプションを提供し、インターフェースは変更できませんが、enable_shared_from_thisから派生したクラスで使用するためのパターンを提供します。このパターンはshared_from(that)です。

注:戻り値の型としてautoを使用する場合は、コンパイラの年数によって異なります。アクションで

template <typename Base> 
inline std::shared_ptr<Base> 
shared_from_base(std::enable_shared_from_this<Base>* base) 
{ 
    return base->shared_from_this(); 
} 
template <typename Base> 
inline std::shared_ptr<const Base> 
shared_from_base(std::enable_shared_from_this<Base> const* base) 
{ 
    return base->shared_from_this(); 
} 
template <typename That> 
inline std::shared_ptr<That> 
shared_from(That* that) 
{ 
    return std::static_pointer_cast<That>(shared_from_base(that)); 
} 

例:

struct base : public std::enable_shared_from_this<base> {}; 
struct derived : public base 
{ 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
    // Can also provide a version for const: 
    auto shared_from_this() const { 
     return shared_from(this); 
    } 
    // Note that it is also possible to use shared_from(...) from 
    // outside the class, e.g. 
    // auto sp = shared_from(that); 
}; 
template <typename X> 
struct derived_x : public derived 
{ 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
}; 

コンパイル試験:

int main() 
{ 
    auto pbase = std::make_shared<base>(); 
    auto pderived = std::make_shared<derived>(); 
    auto pderived_x = std::make_shared<derived_x<int> >(); 

    auto const& const_pderived = *pderived; 
    const_pderived.shared_from_this(); 

    std::shared_ptr<base> test1 = pbase->shared_from_this(); 
    std::shared_ptr<derived> test2 = pderived->shared_from_this(); 
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this(); 

    return 0; 
} 

これは、ライブラリヘッダ内に配置することができるいくつかの外部のシム機能を必要とします

https://onlinegdb.com/SJWM5CYIG

前溶液 - 「通常」クラスとテンプレートクラスのために必要な実装の間に、特に不均一性 - これはいくつかの問題を抱えていた基底クラスの関数を置きました。
また、すべてのDRYではない新しいクラス階層に対して、基本クラスの実装を繰り返す必要があります。 さらに、基本クラス関数は、異なるオブジェクトから基本クラスポインタを供給することによって誤用の可能性がありました。上のより新しいスキームは、これを完全に回避し、ランタイムアサート(...)チェックが行われます。

旧実装:

#include <cassert> 
#include <memory> 

class base : public std::enable_shared_from_this<base> 
{ 
protected: 
    template <typename T> 
    std::shared_ptr<T> shared_from(T* derived) { 
     assert(this == derived); 
     return std::static_pointer_cast<T>(shared_from_this()); 
    } 
}; 

class derived : public base 
{ 
public: 
    auto shared_from_this() { 
     return shared_from(this); 
    } 
}; 

template <typename X> 
class derived_x : public derived 
{ 
public: 
    auto shared_from_this() { 
     return this->template shared_from(this); 
    } 
}; 

int main() 
{ 
    auto pbase = std::make_shared<base>(); 
    auto pderived = std::make_shared<derived>(); 
    auto pderived_x = std::make_shared<derived_x<int> >(); 

    std::shared_ptr<base> test1 = pbase->shared_from_this(); 
    std::shared_ptr<derived> test2 = pderived->shared_from_this(); 
    std::shared_ptr<derived_x<int> > test3 = pderived_x->shared_from_this(); 

    return 0; 
} 
+0

'derived_x'に' template'キーワードを使ったのはなぜですか? – Myon

+1

@マイヨンこれは、shared_fromが基本クラスのテンプレートメンバ関数であり、テンプレートクラス内から呼び出されているためです。ここの 'this'または 'template'キーワードがなければ、準拠したコンパイラではコンパイルできません。 MSVCの古いバージョンはおそらくこの罰金をコンパイルするでしょう。 コンパイラは '<'記号を 'より小さい'演算子として解釈します。現在のドラフトのC++標準文書のセクション17.2.4(312ページ)を参照してください:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2017/n4713.pdf、 – Pete

+0

@Myonが実際に来ました醜いテンプレートクラスの不均一性を完全に避け、さらに多くの他の肯定的な副作用があります。 – Pete

関連する問題