ここではstatic_cast<>
、dynamic_cast<>
具体的には、それらがポインタに関連する場合の概要を示します。これは単なる101レベルのランダウンであり、すべての複雑さをカバーしているわけではありません。
static_cast <Type *>(ptr)
これはポインタを受け取り、ptr
それをタイプのポインタに安全にキャストしようとしますType*
。このキャストはコンパイル時に行われます。タイプタイプが関連している場合にのみキャストを実行します。タイプが関連していない場合、コンパイラエラーが発生します。例えば:
class B {};
class D : public B {};
class X {};
int main()
{
D* d = new D;
B* b = static_cast<B*>(d); // this works
X* x = static_cast<X*>(d); // ERROR - Won't compile
return 0;
}
dynamic_cast <Type *>(ptr)
これは再びポインタを取り込み、ptr
安全にそれをtypeのポインタにキャストしようとしますType*
。ただし、このキャストはコンパイル時ではなく実行時に実行されます。これはランタイムキャストであるため、特にポリモーフィッククラスと組み合わせると便利です。実際、公認の場合、キャストが合法であるためには、クラスはポリモーフィックでなければなりません。
キャストは、ベースから派生(B2D)または派生からベース(D2B)のいずれかの方向に進むことができます。実行時にD2Bキャストがどのように機能するかを確認するのは簡単です。ptr
由来Type
かそうでないかのどちらかです。D2B dynamic_cast <> sの場合、ルールは単純です。何かを他のものにキャストしようとすることができptr
、実際にから派生したType
場合、Type*
からポインターが返されdynamic_cast
ます。それ以外の場合は、NULLポインターを取得します。
ただし、B2Dキャストはもう少し複雑です。次のコードを検討してください。
#include <iostream>
using namespace std;
class Base
{
public:
virtual void DoIt() = 0; // pure virtual
virtual ~Base() {};
};
class Foo : public Base
{
public:
virtual void DoIt() { cout << "Foo"; };
void FooIt() { cout << "Fooing It..."; }
};
class Bar : public Base
{
public :
virtual void DoIt() { cout << "Bar"; }
void BarIt() { cout << "baring It..."; }
};
Base* CreateRandom()
{
if( (rand()%2) == 0 )
return new Foo;
else
return new Bar;
}
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = (Bar*)base;
bar->BarIt();
}
return 0;
}
main()
CreateRandom()
返されるオブジェクトの種類がわからないため、CスタイルのキャストBar* bar = (Bar*)base;
は明らかにタイプセーフではありません。どうすればこれを修正できますか?1つの方法は、boolなどの関数をAreYouABar() const = 0;
基本クラスに追加し、true
from Bar
とfalse
from を返すことFoo
です。しかし、別の方法がありますdynamic_cast<>
。
int main()
{
for( int n = 0; n < 10; ++n )
{
Base* base = CreateRandom();
base->DoIt();
Bar* bar = dynamic_cast<Bar*>(base);
Foo* foo = dynamic_cast<Foo*>(base);
if( bar )
bar->BarIt();
if( foo )
foo->FooIt();
}
return 0;
}
キャストは実行時に実行され、オブジェクトを照会することで機能し(今のところ、その方法を気にする必要はありません)、探している型かどうかを尋ねます。そうであればdynamic_cast<Type*>
、ポインタを返します。それ以外の場合はNULLを返します。
このベースから派生したキャストがを使用して機能するためにはdynamic_cast<>
、Base、Foo、およびBarが標準でポリモーフィック型と呼ばれている必要があります。ポリモーフィック型になるには、クラスに少なくとも1つのvirtual
関数が必要です。クラスがポリモーフィック型ではない場合、ベースから派生したの使用はdynamic_cast
コンパイルされません。例:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
仮想dtorなどの仮想関数をベースに追加すると、BaseとDerの両方のポリモーフィックタイプが作成されます。
class Base
{
public:
virtual ~Base(){};
};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // OK
return 0;
}
dynamic_cast<>
舞台裏でどのように機能するか(またはC ++ がどの程度機能するか)の良いアイデアを得たい場合は、Lippmanの「Inside the C ++ Object Model」という優れた本(これも非常に技術的に読むのは簡単です)です。また、Stroustrupの「C ++の設計と進化」および「C ++プログラミング言語」の本は優れたリソースですが、リップマンの本は、C ++が「舞台裏」でどのように機能するかを扱っています。