Oto podsumowanie, static_cast<>
a dynamic_cast<>
konkretnie, jak odnoszą się one do wskaźników. To tylko lista 101 poziomów, nie obejmuje wszystkich zawiłości.
static_cast <Type *> (ptr)
To pobiera wskaźnik ptr
i próbuje bezpiecznie rzutować go na wskaźnik typu Type*
. To rzutowanie jest wykonywane w czasie kompilacji. Wykonuje rzutowanie tylko wtedy, gdy typy typów są powiązane. Jeśli typy nie są powiązane, pojawi się błąd kompilatora. Na przykład:
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)
To ponownie próbuje wziąć wskaźnik ptr
i bezpiecznie rzucić go na wskaźnik typu Type*
. Ale to rzutowanie jest wykonywane w czasie wykonywania, a nie w czasie kompilacji. Ponieważ jest to rzutowanie w czasie wykonywania, jest przydatne zwłaszcza w połączeniu z klasami polimorficznymi. W rzeczywistości w pewnych przypadkach klasy muszą być polimorficzne, aby obsada była legalna.
Rzuty mogą przebiegać w jednym z dwóch kierunków: od podstawy do pochodnej (B2D) lub od pochodnej do podstawy (D2B). Wystarczy zobaczyć, jak rzutowanie D2B będzie działać w czasie wykonywania. Albo ptr
pochodzi z, Type
albo nie. W przypadku D2B dynamic_cast <> s, zasady są proste. Możesz spróbować rzucić cokolwiek na cokolwiek innego, a jeśli ptr
faktycznie zostało wyprowadzone z Type
, otrzymasz Type*
wskaźnik z powrotem dynamic_cast
. W przeciwnym razie otrzymasz wskaźnik NULL.
Ale rzuty B2D są trochę bardziej skomplikowane. Rozważ następujący kod:
#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()
nie można powiedzieć, jaki rodzaj obiektu CreateRandom()
zwróci, więc rzutowanie w stylu C Bar* bar = (Bar*)base;
zdecydowanie nie jest bezpieczne dla typów. Jak mogłeś to naprawić? Jednym ze sposobów byłoby dodać funkcję jak bool AreYouABar() const = 0;
do klasy bazowej i powrót true
z Bar
i false
z Foo
. Ale jest inny sposób: użyj 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;
}
Rzuty są wykonywane w czasie wykonywania i działają poprzez odpytywanie obiektu (na razie nie trzeba się martwić, jak to zrobić), pytając, czy jest to typ, którego szukamy. Jeśli tak, dynamic_cast<Type*>
zwraca wskaźnik; w przeciwnym razie zwraca NULL.
Aby to rzutowanie z bazy na wyprowadzenie działało przy użyciu dynamic_cast<>
, Base, Foo i Bar muszą być tym, co Standard nazywa typami polimorficznymi . Aby być typem polimorficznym, Twoja klasa musi mieć co najmniej jedną virtual
funkcję. Jeśli Twoje klasy nie są typami polimorficznymi, użycie metody bazowej na pochodne dynamic_cast
nie zostanie skompilowane. Przykład:
class Base {};
class Der : public Base {};
int main()
{
Base* base = new Der;
Der* der = dynamic_cast<Der*>(base); // ERROR - Won't compile
return 0;
}
Dodanie funkcji wirtualnej do bazy, takiej jak wirtualny dtor, spowoduje utworzenie polimorficznych typów Base i 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<>
działa za kulisami (lub jak dużo C ++ działa), dobrą książką (która jest również dość łatwa do przeczytania w przypadku czegoś tak technicznego) jest „Inside the C ++ Object Model” Lippmana. Również książki Stroustrupa „Design and Evolution of C ++” i „The C ++ Programming Language” są dobrymi zasobami, ale książka Lippman jest poświęcona temu, jak C ++ działa „za kulisami”.