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.

 


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