Funkcja szablonowa dynamic_cast umożliwia bezpieczne rzutowanie wskaźnika na klasę lub referencji w ramach hierarchii dziedziczenia. Gdy dany wskaźnik nie może zostać zrzutowany do danego typu to zwracana wartość to NULL lub (w C++ 11) nullptr. Oto składnia funkcji dynamic_cast:
typ_rzutowania musi być wskaźnikiem lub typem referencyjnym;
rzutowany_obiekt musi być wskaźnikiem lub zmienną statyczną;
Rzutowanie w dół hierarchii klasy jest szczególnie niebezpiecznie gdyż dana klasa bazowa może być dziedziczona przez wiele typów klas. Z tego względu konieczne jest rozróżnianie, czy za danym wskaźnikiem czy referencją klasy bazowej rzeczywiście stoi klasa, na której wskaźnik lub referencję wykonywane jest rzutowanie. Oto prosty przykład:
#include <iostream>
using namespace std;
// abstrakcyjna klasa (interfejs)
class InterfaceClass{
public:
virtual void fu() = 0;
};
// klasy dziedziczące po klasie abstrakcyjnej
class ConcreteA : public InterfaceClass {
public:
virtual void fu() { cout << "ConcreteA fu is called"; }
void fuConcreteA() { cout << "fuConcreteA is called"; }
};
class ConcreteB : public InterfaceClass {
public:
virtual void fu() { cout << "ConcreteB fu is called"; }
void fuConcreteB() { cout << "fuConcreteB is called"; }
};
int main(int argc, char *argv[])
{
// tworzę wskaźnik na interfejs klasy InterfaceClass, za którym stoi klasa ConcreteA
InterfaceClass *interface = new ConcreteA;
// rzutowanie wskaźnika na ConcreteB
ConcreteB *b = dynamic_cast<ConcreteB*>(interface);
// jeżeli b != 0 to
if(b){
b->fuConcreteB(); // mogę wywołać metodę wewnętrzną klasy ConcreteB
}
// rzutowanie wskaźnika na ConcreteA
ConcreteA *a = dynamic_cast<ConcreteA*>(interface);
if(a){
a->fuConcreteA(); // mogę wywołać metodę wewnętrzną klasy ConcreteA
}
return 0;
}
Powyższy kod wyświetli oczywiście:
fuConcreteA is called
Możliwe jest również rzutowanie z wykorzystaniem referencji:
#include <iostream>
using namespace std;
class InterfaceClass{
public:
virtual void fu() = 0;
};
// klasy polimorficzne
class ClassA : public InterfaceClass{
public:
void fuClassA() { cout << "fuConcreteA is called" << endl; }
};
class ClassB : public InterfaceClass{
public:
void fuClassB() { cout << "fuConcreteB is called" << endl; }
};
// klasa bazowa
class ClassC : public InterfaceClass, public ClassA, public ClassB {
public:
void fu() { cout << "Base class"; }
};
int main(int argc, char *argv[])
{
// tworzę obiekt klasy bazowej
ClassC base;
ClassA &a = base; // przepisuję jego referencje do obiektu klasy CassA
ClassB &b = dynamic_cast<ClassB&>(a); // wyciągam referencje do obiektu klasy B (rzutowanie w bok)
b.fuClassB();
ClassC &c = dynamic_cast<ClassC&>(a); // rzutowanie w górę do klasy bazowej
c.fu();
return 0;
}