Rzutowanie w dół i rzutowanie w górę za pomocą funkcji dynamic_cast

Autor podstrony: Krzysztof Zajączkowski

Stronę tą wyświetlono już: 4281 razy

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 dynamic_cast < typ_rzutowania > ( rzutowany_obiekt );

gdzie:

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; }