C++. Динамічна ідентифікація типів. Оператор typeid()

Динамічна ідентифікація типів. Оператор typeid. Приклади


Зміст


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

1. Поняття про динамічну ідентифікацію типу

З метою підтримки динамічного поліморфізму у мову C++ введно динамічну ідентифікацію типу (RTTI – Run-Time Type Identification). Ця динамічна ідентифікація дозволяє ідентифікувати тип об’єкту при виконанні програми. Основним засобом динамічної ідентифікації типу є оператор typeid().

З метою забезпечення кращого приведення типів, в останні версії C++ включено додаткові оператори приведення типу. До цих операторів належать:

  • dynamic_cast;
  • const_cast;
  • reinterpret_cast;
  • static_cast.

Також мова C++ має стандартний оператор приведення типу, який має вигляд

(type)expression

Більш детальну інформацію про цей оператор приведення типу можна отримати (тут).

 

2. Ідентифікація типу. Операор typeid

Мова програмування C++ підтримує динамічний поліморфізм завдяки поєднанню спадковості, віртуальних функцій та використання покажчика (посилання) на базовий клас. В ієрархії класів покажчик на базовий клас може вказувати на екземпляр будь-якого класу з ієрархії. Тому, не завжди можна передбачити, на об’єкт якого класу ієрархії вказує покажчик. Ідентифікацію типу потрібно здійснювати по ходу виконання програми.

Для ідентифікації типу у мові C++ використовується оператор typeid(). Цей оператор має дві форми використання:

  • typeid(object) – визначення характеристик типу об’єкту або змінної з іменем object;
  • typeid(type) – визначення характеристик типу з іменем type.

Оператор typeid() повертає посилання на об’єкт типу type_info. Клас type_info описує тип об’єкту і має наступні внутрішні члени:

  1. Операторні функції
bool operator==(const type_info& obj);
bool operator!=(const type_info& obj);

Ці функції перевантажують оператори порівняння == і !=.

  1. Функцію
const char* name();

Ця функція повертає покажчик на ім’я типу, який визначається.

  1. Функцію
bool before(const type_info& obj);

Ця функція повертає true, якщо викликаючий об’єкт передує об’єкту, що був використаний в якості параметру.

Щоб використати оператор typeid() потрібно підключити модуль typeinfo

#include <typeinfo>

 

2.1. Оператор typeid для об’єкту
2.1.1. Приклад використання оператора typeid() для змінних базових типів та об’єктів класів

 

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

// Деякий клас
class MyClass
{
  // ...
};

void main()
{
  // Отримати характеристики типу на основі об'єкту (екземпляру)
  // 1. Оголосити змінні (об'єкти) різних типів
  int a, b;
  double x;
  string s;
  MyClass obj;

  // 2. Використати посилання на type_info для отримання інформації про тип
  // 2.1. Метод name() - отримати ім'я типу
  const type_info& ti = typeid(x);
  cout << "Type of x is: " << ti.name() << endl;

  // 2.2. Порівняти типи об'єктів s та obj
  const type_info& ti_s = typeid(s);
  const type_info& ti_obj = typeid(obj);

  if (ti_s == ti_obj)
    cout << "typeid(s)==typeid(obj)" << endl;
  else
    cout << "typeid(s)!=typeid(obj)" << endl;

  // 3. Безпосереднє використання typeid() без оголошення посилання
  // 3.1. Порівняти типи об'єктів a, b - використати функцію operator==()
  if (typeid(a) == typeid(b))
    cout << "Types of a and b are equal." << endl;
  else
    cout << "Types of a and b are not equal." << endl;

  // 3.2. Порівняти типи змінних x та a - використати функцію operator!=()
  if (typeid(x) != typeid(a))
    cout << "Types of x and a are not equal." << endl;
  else
    cout << "Types of x and a are equal." << endl;

  // 3.3. Отримати дані на основі типу
  string tName = typeid(s).name();
  cout << "typeid(string) = " << tName << endl;
}

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

Type of x is: double
typeid(s)!=typeid(obj)
Types of a and b are equal.
Types of x and a are not equal.
typeid(string) = class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> >

 

2.1.2. Приклад використання оператора typeid для об’єктів класів, що утворюють ієрархію

Оператор typeid() корисний у випадку, коли класи утворюють ієрархію і в класах реалізовані віртуальні функції. У нижченаведеному прикладі оголошується базовий клас Area та два похідні від нього класи Circle та Rectangle. Всі 3 класи містять реалізацію віртуальної функції GetArea(), яка повертає різні значення площі в залежності від обраної фігури.

В демонстраційний цілях реалізована функція Demo_ref_typeid(), яка отримує посилання на базовий клас Area. У функції викликється оператор typeid(), який визначає тип екземпляру, що був переданий у функцію. Тип екземпляру залежить від того, який екземпляр з ієрархії класів був переданий при виклику функції Demo_ref_typeid().

У функції main() демонструється використання оператора typeid() для об’єктів класів та передачу їх у функцію Demo_ref_typeid().

 

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

// Застосування typeid до ієрархії класів
// поліморфний клас Area - площа
class Area
{
public:
  virtual double GetArea() { return 0; }; // віртуальна функція
};

// клас коло
class Circle :public Area
{
  double r;

public:
  Circle(double _r)
  {
    if (_r > 0)
      r = _r;
    else
      r = 1;
  }

  double GetArea()
  {
    const double pi = 3.1415;
    return pi * r * r;
  }
};

// клас прямокутник
class Rectangle :public Area
{
  double a, b;

public:
  Rectangle(double _a, double _b)
  {
    if ((_a >= 0) && (_b >= 0))
    {
      a = _a; b = _b;
    }
    else
    {
      a = b = 0.0;
    }
  }

  double GetArea()
  {
    return a * b;
  }
};

// Передача в метод посилання на базовий клас
// якщо клас поліморфний, то тип посилання визначається типом параметру-об'єкту
void Demo_ref_typeid(Area& ref)
{
  cout << "The type of ref is: ";
  cout << typeid(ref).name() << endl;
}

void main(void)
{
  Area* p; // покажчик на базовий клас
  Area a;
  Circle c(3);
  Rectangle r(7, 5);

  // так неможна, неініціалізоване значення p
  // cout << typeid(*p).name() << endl;

  p = &a;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Area

  p = &c;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Circle

  p = &r;
  cout << "Pointer p points to object of type: ";
  cout << typeid(*p).name() << endl; // class Rectangle

  // Важливо: якщо забрати слово virtual в класі Area,
  // то виведе наступний результат:
  // Pointer p points to object of type: class Area
  // Pointer p points to object of type: class Area
  // Pointer p points to object of type: class Area

  // Демонстрація використання typeid для посилань
  Demo_ref_typeid(a); // The type of ref is: class Area
  Demo_ref_typeid(c); // The type of ref is: class Circle
  Demo_ref_typeid(r); // The type of ref is: class Rectangle
}

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

Реалізація функції Demo_ref_typeid() отримує параметром посилання на базовий клас Area&. Такий самий результат може бути досягнутий при отриманні параметром покажчика на базовий клас Area*.

Після запуску програма видасть наступний результат

Pointer p points to object of type: class Area
Pointer p points to object of type: class Circle
Pointer p points to object of type: class Rectangle
The type of ref is: class Area
The type of ref is: class Circle
The type of ref is: class Rectangle

 

2.2. Оператор typeid для типу (класу)

У прикладі приводиться програмний код, що використовує оператор typeid() для того, щоб визначити тип об’єкту деякого класу та змінної.

 

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

// Набір класів
// Базовий клас
class A
{
  // ...
};

// Похідні класи
class B : public A
{
  // ...
};

class C : public B
{
  // ...
};

void main(void)
{
  // 1. Створити екземпляри класів B, C
  B objB;
  C objC;

  // 2. Перевірка, до якого типу належить екземпляр objC
  if (typeid(objC) == typeid(A))
    cout << "objC is of type A" << endl;
  if (typeid(objC) == typeid(B))
    cout << "objC is of type B" << endl;
  if (typeid(objC) == typeid(C))
    cout << "objC is of type C" << endl; // +

  // 3. Перевірка, чи змінна a цілого типу int
  int a;

  if (typeid(a) == typeid(int))
    cout << "Variable a is of type int." << endl;
  else
    cout << "Variable a is not of type int" << endl;
}

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

objC is of type C
Variable a is of type int.

 

3. Приклад застосування оператора typeid для шаблонних класів

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

Нижче наведено приклад використання оператора typeid для шаблонного класу Point<T>.

 

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

// Шаблонний клас, що реалізує точку на координатній площині
template <class T>
class Point
{
private:
  T x, y;

public:
  // Конструктор
  Point(T _x, T _y) : x(_x), y(_y) { }

  // Методи доступу
  T X() { return x; }
  T Y() { return y; }
};

void main(void)
{
  // Оголосити екземпляри класу Point<T> для різних типів
  Point<int> objInt(8, 9);
  Point<double> objDouble(7.2, 3.1);

  // Вивести типи об'єктів objInt та objDouble
  cout << "typeid(objInt) = " << typeid(objInt).name() << endl;
  cout << "typeid(objDouble) = " << typeid(objDouble).name() << endl;

  // Визначити, чи типи об'єктів objInt та objDouble однакові
  if (typeid(objInt) == typeid(objDouble))
    cout << "Types of objInt and objDouble are equal." << endl;
  else
    cout << "Types of objInt and objDouble are not equal." << endl;
}

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

typeid(objInt) = class Point<int>
typeid(objDouble) = class Point<double>
Types of objInt and objDouble are not equal.

 


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