Динамическая идентификация типов. Оператор typeid(). Примеры
Содержание
- 1. Понятие о динамической идентификации типа
- 2. Идентификация типа. Операор typeid
- 3. Пример применения оператора 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 описывает тип объекта и имеет следующие внутренние члены:
- Операторные функции
bool operator==(const type_info& obj); bool operator!=(const type_info& obj);
Эти функции перегружают операторы сравнения == и !=.
- Функцию
const char* name();
Эта функция возвращает указатель на имя определяемого типа.
- Функцию
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.
⇑
Связанные темы
⇑