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

 


Связанные темы