MyTetra Share
Делитесь знаниями!
Приведение типов в C++
Время создания: 01.04.2016 23:42
Автор: Елена Сагалаева
Текстовые метки: c++, тип, приведение, преобразование, const, cast, static, dynamic, reinterpret
Раздел: Компьютер - Программирование - Язык C++ (Си++)
Запись: xintrea/mytetra_syncro/master/base/14595433404md1knxofb/text.html на raw.github.com

Лучшая практика по приведению типов: не делать этого. Потому что, если в программе потребовалось приведение типов, значит в этой программе с большой долей вероятности что-то неладно. Для довольно редких ситуаций, когда это все-таки действительно нужно, есть четыре способа приведения типов. Старый, оставшийся со времен C, но все еще работающий, лучше не использовать вовсе. Хотя бы потому, что конструкцию вида (Тип) очень сложно обнаружить при чтении кода программы.


const_cast

Самое простое приведение типов. Убирает так называемые cv спецификаторы (cv qualifiers), то есть const и volatile. volatile встречается не очень часто, так что более известно как приведение типов, предназначенное для убирания const. Если приведение типов не удалось, выдается ошибка на этапе компиляции. При использовании остальных приведений типов cv спецификаторы останутся как были.


const char *str = "hello";

char *str1 = const_cast<char*>(str);



Updated 20.07.2008

Пример несколько неудачен. Если снимать const с переменной, которая изначально была const, то дальнейшее её использование приведёт к undefined behaviour. Вот хороший пример:


int i;

const int * pi = &i;

// *pi имеет тип const int,

// но pi указывает на int, который константным не является

int* j = const_cast<int *> (pi);


static_cast

Может быть использован для приведения одного типа к другому. Если это встроенные типы, то будут использованы встроенные в C++ правила их приведения. Если это типы, определенные программистом, то будут использованы правила приведения, определенные программистом.

static_cast между указателями корректно только в том случае, если один из указателей - это указатель на void или если это приведение между объектами классов, где один класс является наследником другого. То есть для приведения к какому-либо типу от void*, который возвращает malloc, следует использовать static_cast.


int * p = static_cast<int*>(malloc(100));



Если приведение не удалось, возникнет ошибка на этапе компиляции. Однако, если это приведение между указателями на объекты классов вниз по иерархии и оно не удалось, результат операции undefined. То есть, срабатывает такое приведение: static_cast<Derived*>(pBase), даже если pBase не указывает на Derived (derived - это производный класс), но программа при этом будет вести себя странно.


dynamic_cast

Безопасное приведение по иерархии наследования, в том числе и для виртуального наследования.



dynamic_cast<derv_class *>(base_class_ptr_expr)



Используется RTTI (Runtime Type Information), чтобы привести один указатель на объект класса к другому указателю на объект класса. Классы должны быть полиморфными, то есть в базовом классе должна быть хотя бы одна виртуальная функция. Если эти условие не соблюдено, ошибка возникнет на этапе компиляции. Если приведение невозможно, то об этом станет ясно только на этапе выполнения программы и будет возвращен NULL.



dynamic_cast<derv_class &>(base_class_ref_expr)



Работа со ссылками происходит почти как с указателями, но в случае ошибки во время исполнения будет выброшено исключение bad_cast.



reinterpret_cast


Самое нахальное приведение типов. Не портируемо, результат может быть некорректным, никаких проверок не делается. Считается, что вы лучше компилятора знаете как на самом деле обстоят дела, а он тихо подчиняется. Не может быть приведено одно значение к другому значению. Обычно используется, чтобы привести указатель к указателю, указатель к целому, целое к указателю. Умеет также работать со ссылками.



reinterpret_cast<whatever *>(some *)

reinterpret_cast<integer_expression>(some *)

reinterpret_cast<whatever *>(integer_expression)



Чтобы использовать reinterpret_cast нужны очень и очень веские причины. Используется, например, при приведении указателей на функции.

Что делает приведение типов в стиле С: пытается использовать static_cast, если не получается, использует reinterpret_cast. Далее, если нужно, использует const_cast .


Примеры

unsigned* и int* никак не связаны между собой. Есть правило приведения между unsigned (int) и int, но не между указателями на них. И привести их с помощью static_cast не получится, придется использовать reinterpret_cast. То есть вот так работать не будет:


unsigned* v_ptr;

cout << *static_cast<int*>(v_ptr) <<endl;


Приведение вниз по иерархии:


class Base { public: virtual ~Base(void) { } };

class Derived1 : public Base { };

class Derived2 : public Base { };

class Unrelated { };


Base* pD1 = new Derived1;


Вот такое приведение корректно: dynamic_cast<Derived1 *>(pD1);

А вот такое возвратит NULL: dynamic_cast<Derived2 *>(pD1);

Никак не связанные указатели можно приводить с помощью reinterpret_cast:


Derived1 derived1;

Unrelated* pUnrelated = reinterpret_cast<Unrelated*>(&derived1);


Пример использования static_cast:


int* pi;

void* vp = pi;

char* pch = static_cast<char*>(vp);


Примеры использования reinterpret_cast:


float f (float);

struct S {

float x;

float f (float);

} s;

void g () {

reinterpret_cast<int *>(&s.x);

reinterpret_cast<void (*) ()>(&f);

reinterpret_cast<int S::*>(&S::x);

reinterpret_cast<void (S::*) ()>(&S::f);

reinterpret_cast<void**>(reinterpret_cast<long>(f));

}


Приведение в стиле C можно использовать, чтобы избавиться от значения, возвращаемого функцией. Польза от этого сомнительная, правда...



string sHello("Hello");

(void)sHello.size(); // Throw away function return



Также я видела использование приведение типов в стиле С для приведения к приватному базовому классу, но для этого можно использовать и reinterpret_cast.


Так же в этом разделе:
 
MyTetra Share v.0.65
Яндекс индекс цитирования