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

Stronę tą wyświetlono już: 64 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:

Listing 1
  1. typ_rzutowania dynamic_cast < typ_rzutowania > ( rzutowany_obiekt );

gdzie:

  • 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:

Listing 2
  1. #include <iostream>
  2. using namespace std;
  3. // abstrakcyjna klasa (interfejs)
  4. class InterfaceClass{
  5. public:
  6. virtual void fu() = 0;
  7. };
  8. // klasy dziedziczące po klasie abstrakcyjnej
  9. class ConcreteA : public InterfaceClass {
  10. public:
  11. virtual void fu() { cout << "ConcreteA fu is called"; }
  12. void fuConcreteA() { cout << "fuConcreteA is called"; }
  13. };
  14. class ConcreteB : public InterfaceClass {
  15. public:
  16. virtual void fu() { cout << "ConcreteB fu is called"; }
  17. void fuConcreteB() { cout << "fuConcreteB is called"; }
  18. };
  19. int main(int argc, char *argv[])
  20. {
  21. // tworzę wskaźnik na interfejs klasy InterfaceClass, za którym stoi klasa ConcreteA
  22. InterfaceClass *interface = new ConcreteA;
  23. // rzutowanie wskaźnika na ConcreteB
  24. ConcreteB *b = dynamic_cast<ConcreteB*>(interface);
  25. // jeżeli b != 0 to
  26. if(b){
  27. b->fuConcreteB(); // mogę wywołać metodę wewnętrzną klasy ConcreteB
  28. }
  29. // rzutowanie wskaźnika na ConcreteA
  30. ConcreteA *a = dynamic_cast<ConcreteA*>(interface);
  31. if(a){
  32. a->fuConcreteA(); // mogę wywołać metodę wewnętrzną klasy ConcreteA
  33. }
  34. return 0;
  35. }

Powyższy kod wyświetli oczywiście:

fuConcreteA is called

Możliwe jest również rzutowanie z wykorzystaniem referencji:

Listing 3
  1. #include <iostream>
  2. using namespace std;
  3. class InterfaceClass{
  4. public:
  5. virtual void fu() = 0;
  6. };
  7. // klasy polimorficzne
  8. class ClassA : public InterfaceClass{
  9. public:
  10. void fuClassA() { cout << "fuConcreteA is called" << endl; }
  11. };
  12. class ClassB : public InterfaceClass{
  13. public:
  14. void fuClassB() { cout << "fuConcreteB is called" << endl; }
  15. };
  16. // klasa bazowa
  17. class ClassC : public InterfaceClass, public ClassA, public ClassB {
  18. public:
  19. void fu() { cout << "Base class"; }
  20. };
  21. int main(int argc, char *argv[])
  22. {
  23. // tworzę obiekt klasy bazowej
  24. ClassC base;
  25. ClassA &a = base; // przepisuję jego referencje do obiektu klasy CassA
  26. ClassB &b = dynamic_cast<ClassB&>(a); // wyciągam referencje do obiektu klasy B (rzutowanie w bok)
  27. b.fuClassB();
  28. ClassC &c = dynamic_cast<ClassC&>(a); // rzutowanie w górę do klasy bazowej
  29. c.fu();
  30. return 0;
  31. }

Komentarze