Приведение типов. RTTI — различия между версиями

Материал из SEWiki
Перейти к: навигация, поиск
(Новая страница: «==Приведение типов== Приведение типов делится на C style cast и C++ style cast ===Приведение в стиле C=== С…»)
 
м (dynamic_cast)
 
(не показано 6 промежуточных версий 3 участников)
Строка 26: Строка 26:
 
Рассмотрим первые три более подробно (dynamic_cast рассматривается в разделе RTTI).
 
Рассмотрим первые три более подробно (dynamic_cast рассматривается в разделе RTTI).
 
====const_cast====
 
====const_cast====
Добавляет/убирает у объекта константность. Если убрать константность у объекта, который был объявлен как константный, то при записи может возникнуть undefined behavior (в зависимости от типа объекта).
+
Добавляет/убирает у объекта квалификаторы 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 бывает полезно.