shared_ptr <Base>をshared_ptr <Derived>にダウンキャストしますか?


102

更新:この例のshared_ptrはBoostのものと似ていますが、それはshared_polymorphic_downcast(またはそのことについてはdynamic_pointer_castまたはstatic_pointer_cast)をサポートしていません!

参照カウントを失うことなく、派生クラスへの共有ポインターを初期化しようとしています:

struct Base { };
struct Derived : public Base { };
shared_ptr<Base> base(new Base());
shared_ptr<Derived> derived;

// error: invalid conversion from 'Base* const' to 'Derived*'
derived = base;  

ここまでは順調ですね。C ++が暗黙的にBase *をDerived *に変換することを期待していませんでした。ただし、私はコードによって表現される機能が必要です(つまり、ベースポインターをダウンキャストしながら参照カウントを維持します)。私の最初の考えは、Baseにキャスト演算子を提供して、Derivedへの暗黙の変換が行われるようにすることでした(ペダントの場合:ダウンキャストが有効であることを確認しますので、心配しないでください)。

struct Base {
  operator Derived* ();
}
// ...
Base::operator Derived* () {
  return down_cast<Derived*>(this);
}

まあ、それは助けにはならなかった。コンパイラーがタイプキャスト演算子を完全に無視したようです。shared_ptr割り当てを機能させる方法はありますか?余分なポイントについて:どのようなタイプBase* constですか? const Base*分かりBase* constますが?constこの場合、何を指しますか?


なぜshared_ptr <Base>ではなくshared_ptr <Derived>が必要なのですか?
ビル・

3
オブジェクトのクローンを作成せずに、BaseにないDerivedの機能にアクセスしたいので(2つの共有ポインターによって参照される単一のオブジェクトが必要です)。ところで、キャスト演算子が機能しないのはなぜですか?
Lajos Nagy

回答:


109

使用できますdynamic_pointer_cast。それはによってサポートされていstd::shared_ptrます。

std::shared_ptr<Base> base (new Derived());
std::shared_ptr<Derived> derived =
               std::dynamic_pointer_cast<Derived> (base);

ドキュメント:https : //en.cppreference.com/w/cpp/memory/shared_ptr/pointer_cast

また、基本クラスでキャスト演算子を使用することはお勧めしません。このような暗黙のキャストは、バグやエラーの原因になる可能性があります。

-更新:タイプがポリモーフィックでstd::static_pointer_castない場合に使用できます。


4
彼が使っていないことを最初の行から理解できませんでしたstd::shared_ptr。しかし、最初の回答のコメントから、彼はブーストを使用していないため、彼はを使用してstd::shared_ptrいる可能性があると推測しました。
Massood Khaari 2013年

OK。ごめんなさい。彼はカスタム実装を使用していることをより明確にする必要があります。
Massood Khaari 2013年

47

私はあなたが使用してboost::shared_ptrいると思います...私はあなたが欲しいと思うかdynamic_pointer_castshared_polymorphic_downcast

ただし、これらには多相型が必要です。

どんなタイプBase* constconst Base*分かりBase* constますが?constこの場合、何を指しますか?

  • const Base *定数への可変ポインタBaseです。
  • Base const *定数への可変ポインタBaseです。
  • Base * constは、可変テーブルへの定数ポインタBaseです。
  • Base const * const定数への定数ポインタBaseです。

これは最小限の例です:

struct Base { virtual ~Base() { } };   // dynamic casts require polymorphic types
struct Derived : public Base { };

boost::shared_ptr<Base> base(new Base());
boost::shared_ptr<Derived> derived;
derived = boost::static_pointer_cast<Derived>(base);
derived = boost::dynamic_pointer_cast<Derived>(base);
derived = boost::shared_polymorphic_downcast<Derived>(base);

あなたの例が基本型のインスタンスを作成してキャストすることが意図的であったかどうかはわかりませんが、違いをうまく説明するのに役立ちます。

static_pointer_cast「ちょうどそれを行う」します。これにより、未定義の動作(Derived*によって割り当てられ、初期化されたメモリを指すBase)が発生し、クラッシュが発生する可能性があります。上の参照カウントbaseが増分されます。

dynamic_pointer_castNULLポインタになります。上の参照カウントbaseは変更されません。

shared_polymorphic_downcast静的キャストと同じ結果になりますが、むしろ成功するために見せかけと未定義の動作につながるよりも、アサーションがトリガされます。上の参照カウントbaseが増分されます。

(デッドリンク)を参照してください:

static_castまたはを使用するかどうかを決定するのが少し難しいdynamic_cast場合があり、両方の世界を少しでも持つことができることを望みます。dynamic_castにはランタイムオーバーヘッドがあることはよく知られていますが、static_castにはオーバーヘッドがないため安全ですが、サイレントに失敗する可能性があります。shared_dynamic_castデバッグビルドやshared_static_castリリースビルドで使用できたら、どんなに素晴らしいでしょう。まあ、そのようなものはすでに利用可能であり、と呼ばれていますshared_polymorphic_downcast


残念ながら、ソリューションは、使用している特定のshared_ptr実装から意図的に除外されたBoost機能に依存しています(理由は尋ねないでください)。constの説明に関しては、今ではもっと意味があります。
Lajos Nagy、

3
他のshared_ptrコンストラクター(static_cast_tagおよびを使用dynamic_cast_tag)を実装する以外に、実行できることはあまりありません。外でshared_ptr行うことは、refcountを管理することができません。-「完全な」オブジェクト指向設計では、常に基本型を使用でき、派生型が何であるかを知る必要も、気にする必要もありません。すべての機能が基本クラスのインターフェースを通じて公開されるためです。おそらく、そもそもなぜダウンキャストする必要があるのか​​を考え直す必要があるだけでしょう。
Tim Sylvester、

1
@Tim Sylvester:しかし、C ++は「完全な」オブジェクト指向言語ではありません!:-)ダウンキャストは不完全なOO言語でその位置を占めています
スティーブフォリー

4

誰かがboost :: shared_ptrでここに来たら...

これは、派生したBoost shared_ptrにダウンキャストする方法です。DerivedがBaseから継承すると仮定します。

boost::shared_ptr<Base> bS;
bS.reset(new Derived());

boost::shared_ptr<Derived> dS = boost::dynamic_pointer_cast<Derived,Base>(bS);
std::cout << "DerivedSPtr  is: " << std::boolalpha << (dS.get() != 0) << std::endl;

「Base」クラス/構造体に少なくとも1つの仮想関数があることを確認してください。仮想デストラクタも機能します。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.