Приведение типов. RTTI — различия между версиями
(Новая страница: «==Приведение типов== Приведение типов делится на C style cast и C++ style cast ===Приведение в стиле C=== С…») |
Zalim (обсуждение | вклад) м (→dynamic_cast) |
||
(не показано 6 промежуточных версий 3 участников) | |||
Строка 26: | Строка 26: | ||
Рассмотрим первые три более подробно (dynamic_cast рассматривается в разделе RTTI). | Рассмотрим первые три более подробно (dynamic_cast рассматривается в разделе RTTI). | ||
====const_cast==== | ====const_cast==== | ||
− | Добавляет/убирает у объекта | + | Добавляет/убирает у объекта квалификаторы const и volatile. Если убрать константность у объекта, который был объявлен как константный, то при записи может возникнуть undefined behavior (в зависимости от типа объекта). |
Рассмотрим ситуацию, когда нам может пригодиться const_cast. | Рассмотрим ситуацию, когда нам может пригодиться const_cast. | ||
Строка 76: | Строка 76: | ||
</source> | </source> | ||
− | + | ===Общее замечание по разделу Приведение типов=== | |
− | Общее замечание по разделу Приведение типов | + | При программировании на С++ рекомендуется использовать только C++-style cast (а еще лучше -- не пользоваться приведением типов)<br/> |
− | При программировании на С++ рекомендуется использовать C-style cast только в случаях приведения элементарных типов друг к другу. | + | <s>При программировании на С++ рекомендуется использовать C-style cast только в случаях приведения элементарных типов друг к другу.</s> |
==RTTI== | ==RTTI== | ||
Строка 106: | Строка 106: | ||
</source> | </source> | ||
− | У класса type_info помимо метода name (возвращающего строку - char*, характеризующую тип объекта) и операторов равенства/неравенства есть еще метод before, который позволяет упорядочивать экземпляры type_info и хранить их например в map. | + | У класса type_info помимо метода name (возвращающего строку - char*, характеризующую тип объекта) и операторов равенства/неравенства есть еще метод before, который позволяет упорядочивать экземпляры type_info и хранить их, например, в map. |
+ | |||
+ | ===dynamic_cast=== | ||
+ | Позволяет приводить указатель или объект одного типа к указателю или ссылке на другой класс (связанный с первым через наследование). | ||
+ | В отличии от static_cast выполняется в run-time. | ||
+ | При ошибке приведения к указателю на тип dynamic_cast возвращает нулевой указатель, при ошибке приведения к ссылке на тип выбрасывается исключение bad_cast. | ||
+ | |||
+ | Пример использования: | ||
+ | <source lang="cpp"> | ||
+ | struct A { | ||
+ | virtual void f() {} | ||
+ | }; | ||
+ | struct B : A { | ||
+ | virtual void f() {} | ||
+ | }; | ||
+ | |||
+ | ... | ||
+ | A* pa = new B; | ||
+ | A* pa2 = new A; | ||
+ | |||
+ | B* pb = dynamic_cast<B*>(pa); // pb указывает на B | ||
+ | B* pb2 = dynamic_cast<B*>(pa2); // pb2 указывает на А, а не на В! pb2 == NULL | ||
+ | |||
+ | pb->f(); | ||
+ | pb2->f(); // ошибка на этапе исполнения! pb2 == NULL | ||
+ | </source> | ||
+ | |||
+ | Замечание: При приведении к указателю на void возвращается указатель на начало блока. | ||
+ | |||
+ | === Общие замечания по разделу RTTI === | ||
+ | * почти всегда можно обойтись без RTTI и чаще всего использование этого механизма говорит об ошибке проектирования | ||
+ | * на этапе отладки применение RTTI бывает полезно. |
Текущая версия на 10:33, 11 июня 2012
Содержание
Приведение типов
Приведение типов делится на C style cast и C++ style cast
Приведение в стиле C
Синтаксис приведения:
- (тип_к_которому_приводим) переменная
- тип_к_которому_приводим (переменная)
Примеры:
int i = 10;
double d = (double) i;
float f = float(i);
char const* ch = "hello";
string str = (string) ch;
Приведение в стиле С++
В С++ для приведения типов используются следующие функции:
- const_cast
- reinterpret_cast
- static_cast
- dynamic_cast
Во всех случаях синтаксис приведения будет выглядеть следующим образом: ..._cast <тип_к_которому_приводим> (переменная)
Рассмотрим первые три более подробно (dynamic_cast рассматривается в разделе RTTI).
const_cast
Добавляет/убирает у объекта квалификаторы const и volatile. Если убрать константность у объекта, который был объявлен как константный, то при записи может возникнуть undefined behavior (в зависимости от типа объекта).
Рассмотрим ситуацию, когда нам может пригодиться const_cast. Предположим у нас класс A, в котором хранятся элементы типа Т. Пусть требуется реализовать метод get (константный и не константный), который по индексу i будет возвращать i-ый элемент. Можно описать реализацию только одного метода (константного), а второй реализовать через вызов первого при помощи const_cast:
T const& get (int i) const;
T& get (int i) {
return const_cast<T&> (const_cast<A const*> (this).get(i) );
}
reinterpret_cast
Приводит любой указатель к любому указателю.
Пример:
struct Point3 {
double x;
double y;
double z;
};
...
Point3* p = ...;
double* p_x = reinterpret_cast<double*> (p);
Замечание: приведенный пример отработает корректно в случае если структура Point3 плотно упакована в памяти (см. опции компилятора).
static_cast
Используется для приведения
- числовых типов (аналогично C-style cast)
- указателей и ссылок для классов связанных наследованием
- пользовательских преобразований
Преобразование выполняется в момент компиляции и run-time проверки (как это есть в dynamic_cast) приведения типов нет, поэтому сложные приведения типов следует делать очень аккуратно.
Пример:
struct A {};
struct B : A {};
...
B* b;
A* a = static_cast<A*> (b);
b = static_cast<B*> (a);
Замечание: static_cast меняет указатель в зависимости от того какое у классов наследование (в отличии от reinterpret_cast), поэтому если будет forward declaration, то static_cast выдаст ошибку (на этапе компиляции):
struct A;
struct B;
B* b;
A* a = static_cast<A*>(b); // ошибка!
Общее замечание по разделу Приведение типов
При программировании на С++ рекомендуется использовать только C++-style cast (а еще лучше -- не пользоваться приведением типов)
При программировании на С++ рекомендуется использовать C-style cast только в случаях приведения элементарных типов друг к другу.
RTTI
RTTI расшифровывается как Run-time type information. Это механизм, позволяющий определять тип объекта в момент выполнения программы. В С++ данный механизм реализуется при помощи следующих элементов:
- оператор dynamic_cast (используется для преобразования полиморфных типов)
- оператор typeid (для точного определения типа объекта)
- класс type_info (для хранения информации, возвращаемой оператором typeid)
Рассмотрим применение и синтаксис typeid и type_info на примере:
#include <typeinfo>
...
A* a;
type_info& ti = typeid(a);
std::cout << ti.name() << std::endl;
Более сложный пример:
#include <typeinfo>
struct A {};
struct B : A {};
...
A* a = new B();
typeid(a); // A*
typeid(*a); // B
У класса type_info помимо метода name (возвращающего строку - char*, характеризующую тип объекта) и операторов равенства/неравенства есть еще метод before, который позволяет упорядочивать экземпляры type_info и хранить их, например, в map.
dynamic_cast
Позволяет приводить указатель или объект одного типа к указателю или ссылке на другой класс (связанный с первым через наследование). В отличии от static_cast выполняется в run-time. При ошибке приведения к указателю на тип dynamic_cast возвращает нулевой указатель, при ошибке приведения к ссылке на тип выбрасывается исключение bad_cast.
Пример использования:
struct A {
virtual void f() {}
};
struct B : A {
virtual void f() {}
};
...
A* pa = new B;
A* pa2 = new A;
B* pb = dynamic_cast<B*>(pa); // pb указывает на B
B* pb2 = dynamic_cast<B*>(pa2); // pb2 указывает на А, а не на В! pb2 == NULL
pb->f();
pb2->f(); // ошибка на этапе исполнения! pb2 == NULL
Замечание: При приведении к указателю на void возвращается указатель на начало блока.
Общие замечания по разделу RTTI
- почти всегда можно обойтись без RTTI и чаще всего использование этого механизма говорит об ошибке проектирования
- на этапе отладки применение RTTI бывает полезно.