2009-09-02 8 views
5

スマートポインタはキャストを処理しますか?そうでない場合、この制限を回避する安全な方法は何ですか?スマートポインタはどのようにキャストダウンされるべきですか?

私がしようとしていることの一例は、スマートポインタを含む2つのSTLベクトル(例えば)です。最初のクラスは基本クラスへのスマートポインタを含み、2番目のクラスは派生クラスへのスマートポインタを含みます。スマートポインタはカウントされて参照される。 Boostのshared_ptrsと同様の動作ですが、ハンドロールします。私は例を提供するために、手早くいくつかのサンプルコードが含まれていました:私は、同じオブジェクトを管理する2つのスマートポインタで終わると思うよう

vector<CBaseSmartPtr> vecBase; 
vector<CDerivedSmartPtr> vecDer; 
... 
CBaseSmartPtr first = vecBase.front(); 
vecDer.push_back(CDerivedSmartPtr(dynamic_cast<CDerived*>(first.get())); 

これは、私には安全ではないようです。トラックのあるポイントでは、おそらく一方がオブジェクトを解放し、もう一方はオブジェクトへの参照を保持します。

私が期待しているが動作しないと思うことは、同じオブジェクトを維持しながら、まっすぐなダウンキャストです。

dynamic_cast<CDerivedSmartPtr>(first) 

私はまたCBaseSmartPtrを使用するための第2の容器を変更するために探しているだけの使用に意気消沈するべきか?他にも解決策はありますか?

+2

なぜあなたはあなた自身の作っていますか? – GManNickG

+1

これは面白い質問です。スマートポインタの実装の内部に到達するからです。しかし、あなたの戦略にダウンキャスティングを組み込むことは、デザインの欠陥、IMHOを意味することも指摘します。 –

+0

なぜ私が自分自身を作っているのかを答えるために、それはレガシーコードであり、現在の使用法を取り除き、Boostで置き換えることはかなり大きな変更になるでしょう。それは私の "今後のこと"のリストにあります。 – dlanod

答えて

5

スマートポインタはダウンキャストを処理できますが、自動ではありません。そして、constの正しさを得ることはちょっと複雑になる可能性があります(私はインタビューの質問でスマートポインタの実装を使用しましたが、いくつかのテンプレートのトリッキーがあります)。しかし、スマートポインタの多くのユーザは、決してconst修飾型のスマートポインタをインスタンス化することはありません。

最初に訂正する必要があるのは、カウンターです。 smart_ptr<Base>smart_ptr<Derived>の間でカウンタを共有する必要がある可能性があるため、カウンタの型は型の引数に依存してはなりません。とにかくこれは大したことではありません。カウンタは単なるsize_tであり、おそらくクラスにラップされています。 (注:代替のスマートポインタの設計がありますが、質問にはカウンタが使用されることが強く示唆されています)

ベースへのキャストはかなり簡単です。したがって、あなたのsmart_ptrには、smart_ptrを取るコンストラクタが必要です。このctorには、static_cast<T*>((U*)0);という行を追加します。これはコードを生成しませんが、TがU(モジュロconst資格)の基底ではない場合のインスタンス化を防ぎます。

他の方法は、明示的なキャストが必要です。 Tのすべての基底をプログラムで列挙できないので、smart_ptr<T>smart_ptr<Base1_of_T>, smart_ptr<Base2_of_T>, ...から派生することができません。したがって、dynamic_cast<smart_ptr<T> >は機能しません。あなた自身のsmart_dynamic_cast<SPT>(smart_ptr<U> const& pU)を提供することができます。これは、SPTを保持する関数として実装するのが最適です。この機能では、単にreturn SPT(dynamic_cast<SPT::value_type*>(&*pU))を実行することができます。

+0

実際、私はあなたがプログラムでベースを列挙できないと思います。 Andrejがこれをどうやって行うのか教えてくれていないのですか? – sbi

+0

本はありますが、ここにはありません。しかし、私はそのようなことを覚えていません。どのような混乱も取り除くだけです。「塩基を列挙する」という目標は、タイプBが与えられたタイプB1..Bnのセットを決定することです。これは、is_base_and_derivedの関係がすべてのタイプB1..Bnと他のタイプには当てはまりません。 – MSalters

+0

@MSalters:私のbrainfart。私は型を列挙し、それらを基底クラスにすることを考えていました...申し訳ありません。 – sbi

0

std::auto_ptrのような通常のスマートポインタは、内部でデータをコピーするときにSTLがスマートポインタのインスタンスを互いに割り当てるときに所有権が移動するため、STLコンテナで使用するのが安全ではありません。代わりにboost::shared_ptrのようなものを使用する必要があります。これは内部的に参照カウントを実装して、何個のスマートポインタインスタンスが参照されていてもオブジェクトが確実に生き続けるようにします。独自のスマートポインタ型を作成する場合は、同様の参照カウントを実装する必要があります。

+0

乾杯、質問にコメントを追加して、参照カウントスマートポインタを使用することを明示します。 – dlanod

2

必要なプロパティは、指差しタイプの共分散です。つまり、D isa Bの場合は、smartptr<D> isa smartptr<B>が必要です。私はこれがC++ではエレガントにサポートされているとは思わないが、常にそうであるように、テンプレート/オーバーロードのハックが利用可能である。

http://www.boost.org/doc/libs/1_39_0/libs/smart_ptr/pointer_cast.htmlは、regularとboost :: smart_ptrで動作する動的キャストを提供します。 Boostを使用したくない場合は、実装から学ぶ必要があります。

+4

+1は 'dynamic_pointer_cast'に、-1はC++には共変な戻り型がありません。ここをクリックしてください:http://www.parashift.com/c++-faq-lite/virtual-functions.html#faq-20.8 –

+0

@Kristo:私はあなたと一緒に両方でいますが、これはまだ非常に良いアドバイスだと思います。 @王王:なぜあなたは偽の声明を削除しないでください?それは、他の人がこれをアップコートすることを容易にします。 – sbi

+0

あなたは正しいです - 私は仮想メソッドが共変な戻り値の型を許していることを認識しませんでした!しかし、ジェネリックタイプ、例えば、 ptr ベクトルは、共分散をサポートしていません(Javaのように、タイプ消去ではありますが)。 –

1

ブーストメーリングリストの1つでスレッドhereに従ってください。 boost :: shared_ptrの場合、スマートポインタのダウンキャストを実装する方法を示しています。 HTH

+0

それはまったく恐ろしく見えます。彼はshared_ptr *をshared_ptr *にキャストしています。 Rainer Deykeの反応も参照してください。 – MSalters

+0

Hmmm、はい。更新していただきありがとうございます。 – Abhay

0

私は、Microsoftのページでこれを見つけた:

std::shared_ptr<base> sp0(new derived); 
    std::shared_ptr<derived> sp1 = 
    std::dynamic_pointer_cast<derived>(sp0);