dynamic_castが必要で、まったく回避できない場合の実際の例を誰かに教えてもらえますか?私が考えることができる例は、一般的にダブルディスパッチで回避することができます。
制約が強すぎる場合は、dynamic_castが一般的に進むべき方法である例も良いでしょう。
「通常、タイプツリーの上下のタイプ間でキャストするために使用される」のではなく、実際の例を見たいと思います。
dynamic_castが必要で、まったく回避できない場合の実際の例を誰かに教えてもらえますか?私が考えることができる例は、一般的にダブルディスパッチで回避することができます。
制約が強すぎる場合は、dynamic_castが一般的に進むべき方法である例も良いでしょう。
「通常、タイプツリーの上下のタイプ間でキャストするために使用される」のではなく、実際の例を見たいと思います。
ダブルディスパッチでは、一方のクラスがもう一方のクラスのメソッドを呼び出す必要があるため、相互作用しているタイプが互いの内部を熟知している必要があります。dynamic_cast
クラスの内部を変更できない場合、または問題のクラスのカプセル化を解除したくない場合に機能します。
つまり、ダブルディスパッチは関係するクラスに侵入しますが、クラスdynamic_cast
のキャストの知識がなくても機能します。
dynamic_cast
呼び出されるターゲットメソッドのオーバーロードがわからない場合にも使用できます。例については、昨日投稿したこの質問を参照してください。
最後に、ダブルディスパッチはそれ自体の頭痛の種なしには実現しません
基本クラスShapeは、すべての派生クラスを認識している必要があるため、循環依存が発生します。Shapeから新しいクラス(たとえばTriangle)を派生させる場合は、Shapeのインターフェイスと、他のすべての派生クラスのインターフェイス/実装を更新する必要があります。場合によっては、これはオプションではありません。Shapeのソースコードがないか、変更する意思がないか、許可されていない可能性があります。
「まったく回避できない」という制約が強すぎます。すべてのC++機能をCでエミュレートできます。この機能を回避するために必要なのは、いわば、C++でそのCコードを使用することだけです。たとえば、1998年の言語標準化以前の深遠な時代に由来するライブラリであるMFCは、独自の種類の動的キャストを提供し、現在も提供しています。
一般的に動的キャストが必要な1つの例は、イベント処理に使用されるようなビジターパターンです。訪問の考え方は、動的キャストを一元化することです。これにより、コード全体に無数の動的キャストが散らばる代わりに、次の1つが存在します。
#include <stdio.h>
void say( char const s[] ) { printf( "%s\n", s ); }
struct Event
{
struct Handler
{
virtual void onEvent( Event& ) = 0;
};
virtual void dispatchTo( Handler& aHandler )
{
aHandler.onEvent( *this );
}
template< class SpecificEvent >
static void dispatch( SpecificEvent& e, Handler& aHandler )
{
typedef typename SpecificEvent::Handler SpecificHandler;
// The single dynamic cast:
if( SpecificHandler* p = dynamic_cast<SpecificHandler*>( &aHandler ) )
{
p->onEvent( e );
}
else
{
e.Event::dispatchTo( aHandler );
}
}
};
struct FooEvent
: Event
{
struct Handler
{
virtual void onEvent( FooEvent& ) = 0;
};
virtual void dispatchTo( Event::Handler& aHandler )
{
dispatch( *this, aHandler );
}
};
struct Plane
: Event::Handler
{
virtual void onEvent( Event& ) { say( "An event!" ); }
};
struct Fighter
: Plane
, FooEvent::Handler // Comment out this line to get "An event!".
{
virtual void onEvent( FooEvent& ) { say( "Foo Fighter!" ); }
};
void doThingsTo( Plane& aPlane )
{
FooEvent().dispatchTo( aPlane );
}
int main()
{
Fighter plane;
doThingsTo( plane );
}
このプログラムの出力はですFoo Fighter!
。
前述のように、これは単純化されています。現実はもう少し厄介になる傾向があります。そして、はるかに多くのコードで。
乾杯&hth。
私たちが使用しているライブラリがあり、それは私たちがいくつかのタイプを派生させることを目的としているとしましょう:
class A {};
class B : public A {};
class C : public B {};
そして、タイプを導出するとき、すべてのケースに共通することがいくつかあります。
class CommonStuff {};
class D : public CommonStuff, public C {};
これで、ライブラリを操作しているときに、タイプA&(またはB&またはC&)をとるコールバックがあります。
void some_func(A& obj);
そして、その関数で多形性の振る舞いを期待していると仮定しますが、CommonStuffのいくつかにアクセスする必要があります。
void some_func(A& obj)
{
CommonStuff& comn = dynamic_cast<CommonStuff&>(obj);
}
AとCommonStuffの間には直接的な相関関係がないため、を使用することはできませんstatic_cast
。reinterpret_cast
これは、スライスを導入するため、明らかに正しい選択ではありません。ここでの唯一のオプションはですdyanmic_cast
。
さて、これは回避できるので、これを一粒の塩と一緒に取ってください。
私は個人的に、ゲームエンジンの特定の部分を操作するために使用しています。他のさまざまなエンティティを派生させる基本エンティティクラスがあります。リンクリストに簡単に保存できるように、それらを基本クラスタイプにキャストしました。リスト内の特定のエントリが特定のエンティティのものであるかどうかを確認したい場合は、そのタイプにdynamic_castします。nullが返される場合は、そうではないことがわかります。
多くの場合、動的関数をAに追加することで、dynamic_cast <A *>(...)を置き換えることができます。ただし、Aがサードパーティライブラリのクラスである場合は、変更できないため、仮想関数を追加できません。それに。したがって、dynamic_castを使用する必要がある場合があります。