C++. Оператори dynamic_cast, const_cast, reinterpret_cast, static_cast

Динамічна ідентифікація типів. Оператори dynamic_cast, const_cast, reinterpret_cast, static_cast. Приклади


Зміст


Пошук на інших ресурсах:

1. Оператор dynamic_cast. Приклад

Оператор dynamic_cast використовується для динамічного приведення типу з одночасною перевіркою коректності приведення. Якщо приведення не може бути виконане, то воно не виконується і оператор повертає нульове значення (nullptr).

Загальна форма оператора dynamic_cast наступна

dynamic_cast<type> (expression);

тут

  • type – результуючий тип;
  • expression – вираз, що приводиться до типу type.

Оператор dynamic_cast є ефективним у випадку приведення типів, що утворюють ієрархію успадкування (полімофних типів).
Якщо оператор dynamic_cast застосовується до покажчиків, то повертається нульовий покажчик (nullptr). Якщо цей оператор застосовується до посилань, то у випадку помилки генерується виключна ситуація bad_cast.

Приклад.

У прикладі демонструється використання оператора dynamic_cast для двох класів A та B, які утворюють ієархію успадкування. Розглядаються різноманітні можливі ситуації використання оператора.

#include <iostream>
#include <typeinfo>
using namespace std;

// Оператор dynamic_cast<> - визначає, чи можна динамічно привести типи.
// Базовий клас
class A
{
public:
  virtual void Show()
  {
    cout << "A::Show()" << endl;
  }
};

// Похідний клас
class B :public A
{
public:
  void Show()
  {
    cout << "B::Show()" << endl;
  }
};

void main(void)
{
  // 1. Оголосити змінні
  A* pA; // покажчик на базовий клас
  B* pB; // покажчик на похідний клас
  A a;
  B b;

  // 2. Використати покажчик pA
  pA = &a; // Так можна
  pA->Show();

  // Також можна, покажчик на базовий клас може вказувати на
  // екземпляр похідного класу.
  pA = &b;
  pA->Show();

  // 3. Використати покажчик pB
  //    Так не можна
  // pB = &a; // заборонено, базовий клас не може розширитись до похідного

  // 4. Використання оператора dynamic_cast.
  // Випадок: покажчик отримує адресу екземпляру класу
  cout << "dynamic_cast<type*>(&obj): " << endl;

  // 4.1. Приведення B* <= A* з перевіркою
  pB = dynamic_cast<B*> (&a); // не можна, pB = nullptr

  if (pB==nullptr)
    cout << "Cast A* <= B* -> Error" << endl; // цей варіант
  else
    cout << "Cast A* <= B* -> Ok" << endl;

  // 4.2. Приведення B* <= B*
  pB = dynamic_cast<B*> (&b); // можна
  if (pB != nullptr)
    cout << "Cast B* <= B* -> Ok" << endl; // цей варіант
  else
    cout << "Cast B* <= B* -> Error" << endl;

  // 4.3. Приведення A* <= A*
  pA = dynamic_cast<A*>(&a); // можна, pA!=nullptr
  if (pA != nullptr)
    cout << "Cast A* <= A* -> Ok" << endl;
  else
    cout << "Cast A* <= A* -> Error";

  // 4.4. Приведення A* <= B*
  pA = dynamic_cast<B*>(&b); // можна
  if (pA)
    cout << "Cast A* <= B* -> Ok" << endl;
  else
    cout << "Cast A* <= B* -> Error";

  cout << "---------------------------------" << endl;

  // 5. Використання оператора dynamic_cast.
  // Випадок: покажчик отримує адресу іншого покажчика
  cout << "dynamic_cast<type*>(ptr -> Type): " << endl;

  // 5.1. Приведення B* <= B*
  // Покажчик pA посилається на об'єкт класу B
  pA = &b; // можна, pA->b
  pB = dynamic_cast<B*>(pA); // можна, тому що pA посилається на об'єкт класу B
  if (pB)
    cout << "Cast B* <= (pA -> B) --> Ok " << endl; // +
  else
    cout << "Cast B* <= (pA -> B) --> Error " << endl;

  // 5.2. Приведення B* <= A*
  // Покажчик pA посилається на об'єкт класу A
  pA = &a; // pA -> A
  pB = dynamic_cast<B*>(pA); // неможна, тому що pA посилається на об'єкт класу A
  if (pB)
    cout << "Cast B* <= (pA -> A) --> Ok " << endl;
  else
    cout << "Cast B* <= (pA -> A) --> Error " << endl; // +

  // 5.3. Приведення A* <= B*
  // Покажчик pB вказує на об'єкт класу B
  pB = &b;
  pA = dynamic_cast<A*> (pB); // можна
  if (pA)
    cout << "Cast A* <= (pB -> B) --> Ok " << endl;
  else
    cout << "Cast A* <= (pB -> B) --> Error" << endl;

  // 5.4. Приведення A* <= A*
  // pB = &a; // помилка компіляції
}

Результат виконання програми

A::Show()
B::Show()
dynamic_cast<type*>(&obj):
Cast A* <= B* -> Error
Cast B* <= B* -> Ok
Cast A* <= A* -> Ok
Cast A* <= B* -> Ok
---------------------------------
dynamic_cast<type*>(ptr -> Type):
Cast B* <= (pA -> B) --> Ok
Cast B* <= (pA -> A) --> Error
Cast A* <= (pB -> B) --> Ok

 

2. Заміна оператора typeid оператором dynamic_cast

Якщо класи утворюють ієрархію (класи є поліморфними), то оператор typeid може бути замінений оператором dynamic_cast.

У прикладі нижче демонструється ця можливість.

#include <iostream>
#include <typeinfo>
using namespace std;

// заміна оператора typeid оператором dynamic_cast
class A
{
public:
  virtual void Show()
  {
    cout << "A::Show()" << endl;
  }
};

class B :public A
{
public:
  void Show()
  {
    cout << "B::Show()" << endl;
  }
};

void main(void)
{
  // 1. Оголошення покажчиків та об'єктів класів
  A* pA; // покажчик на базовий клас
  B* pB; // покажчик на похідний клас
  A a;
  B b;

  // 2. Застосування оператора typeid
  cout << "typeid:" << endl;

  // 2.1. pA->A
  pA = &a;
  if (typeid(*pA) == typeid(B)) // чи можна *pA привести до типу B
  {
    pB = (B*)pA;
    cout << "Cast B <= (pA->A) --> OK" << endl;
  }
  else
    cout << "Cast B <= (pA->A) --> Error" << endl;

  // 2.2. pA->B
  pA = &b;
  if (typeid(*pA) == typeid(B)) // чи можна *pA привести до типу B
  {
    pB = (B*)pA;
    cout << "Cast B <= (pA->B) --> OK" << endl;
  }
  else
    cout << "Cast B <= (pA->B) --> Error" << endl;

  // 3. Застосування оператора dynamic_cast
  cout << "-----------------------------------" << endl;
  cout << "dynamic_cast: " << endl;

  // 3.1. pA->A
  pA = &a;
  pB = dynamic_cast<B*> (pA);
  if (pB)
    cout << "Cast B* <= (pA->A) --> OK" << endl;
  else
    cout << "Cast B* <= (pA->A) --> Error" << endl;

  // 3.2. pA->B
  pA = &b;
  pB = dynamic_cast<B*> (pA);
  if (pB)
    cout << "Cast B* <= (pA->B) --> OK" << endl;
  else
    cout << "Cast B* <= (pA->Error) --> Error" << endl;
}

Результат виконання програми

typeid:
Cast B <= (pA->A) --> Error
Cast B <= (pA->B) --> OK
-----------------------------------
dynamic_cast:
Cast B* <= (pA->A) --> Error
Cast B* <= (pA->B) --> OK

 

3. Оператор const_cast. Приклад

Оператор const_cast використовується, щоб замінити модифікатор const і/або модифікатор volatile.
В цьому операторі тип результату повинен співпадати з вихідним. Оператор const_cast застосовується, щоб позбутися модифікатора const.

Загальний вигляд оператора const_cast наступний

const_cast<type> (expr)

Приклад.

З допомогою оператора const_cast через посилання на константну величину можна зняти модифікатор const з цієї константної величини.

#include <iostream>
using namespace std;

// 1. Застосування const_cast для посилання
void ExampleConstCast1(const int& value)
{
  // зняти специфікатор const, отримати доступ до value
  int& ref = const_cast<int&> (value);
  ref = 20; // через посилання на константну величину можна доступитись до value
  // value = 50; - неможна, expression must be a modifiable lvalue
}

// 2. Застосування const_cast для посилання
void ExampleConstCast2(const int& value)
{
  // зняти модифікатор const з value
  const_cast<int&> (value) = value + value;
}

// 3. Застосування const_cast для покажчика
void ExampleConstCast3(const int* x)
{
  int* p = const_cast<int*> (x); // зняти const з x
  *p = 100;
}

void main()
{
  // Демонстрація використання модифікатора const_cast
  int t = 30;
  ExampleConstCast1(t);
  cout << "t = " << t << endl; // t = 20

  ExampleConstCast2(t);
  cout << "t+t = " << t << endl; // t = 20+20 = 40

  int x = 50;
  ExampleConstCast3(&x);
  cout << "x = " << x << endl; // x = 100
}

Результат виконання програми

t = 20
t+t = 40
x = 100

 

4. Оператор reinterpret_cast. Приклад

Оператор reinterpret_cast призначений для перетворення одного типу в інший, навіть не сумісний тип. Цей оператор може також застосовуватись і до покажчиків будь-яких типів.

Загальна форма

reinterpret_cast<type> (expr)

де

  • type – результуючий тип;
  • expr – вираз, що приводиться до нового типу.

Приклад.

#include <iostream>
using namespace std;

void main()
{
  // 1. Конвертувати char* => int
  int number;
  const char* pStr = "Hello world!";

  // отримати покажчик на str як ціле число
  number = reinterpret_cast<int> (pStr);
  cout << "number = " << number << endl;

  // 2. Конвертувати int => double*,
  //    перетворити ціле число в покажчик
  unsigned int num = 300;
  double* p;
  p = reinterpret_cast<double*> (num);
  cout << "p = " << p << endl;
}

Результат виконання програми

number = 9476928
p = 0000012C

 

5. Оператор static_cast. Приклад

Оператор static_cast використовується для виконання неполіморфного приведення типів без перевірки його коректності. Тип, що приводиться, та тип, що отримується, можуть бути такими, що не утворюють ієрархію успадкування.

Загальна форма оператора наступна

static_cast<type> (expr)

тут

  • type – результуючий тип;
  • expr – вираз, який приводиться до типу type.

Приклад.

#include <iostream>
using namespace std;

void main()
{
  // Застосування оператора static_cast - неполіморфне приведення,
  // використовуєтсья так само як і звичайний оператор приведення

  // 1. Конвертування int => double
  double x;
  int a = 25;
  x = static_cast<double> (a) / 4;
  cout << "x = " << x << endl; // x = 6.25

  // 2. Конвертування double => char
  double z = 51.55;
  char c = static_cast<char> (z); // c = '3'
  cout << "c = " << c << endl;

  // 3. Конвертування double => short
  short s = static_cast<short>(118.232);
  cout << "s = " << s << endl; // s = 118
}

Результат виконання програми

x = 6.25
c = 3
s = 118

 


Споріднені теми