Лучшая практика по приведению типов: не делать этого. Потому что, если в программе потребовалось приведение типов, значит в этой программе с большой долей вероятности что-то неладно. Для довольно редких ситуаций, когда это все-таки действительно нужно, есть четыре способа приведения типов. Старый, оставшийся со времен 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, но программа при этом будет вести себя странно.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.Ссылки по теме:
1996 С++ Drawft Standarts - Expressions
compl.lang.c++.moderated. static_cast vs. reinterpret_cast
borland.public.cppbuilder.vcl.components.using. static_cast, dynamic_cast, reinterpret_cast
compl.lang.c++.moderated. static cast
compl.lang.c++.moderated. New-Style Casts
Technorati tag: C++

Atom feed
24 коммент.:
А зачем Вы пишите это в своём блоге? Памятка для самого себя или для других.
PS: Я читаю. Очень классно. Получше чем у нашего преподавателя…
Поскольку этот вопрос мне задается уже не в первый раз, ответила отдельным постом.
Спасибо.
Вроде всё написано, но вопросов больше чем ответов. К тому же встречаются неточности.
const char *str = "hello";
char *str1 = const_cast<char>(str);
Для начала надо бы
const char * str = "hello";
char * str1 = const_cast<char *>(str);
Аналогично забыты звёздочки ещё в куче мест.
Кроме того так делать категорически нельзя. Память под строковые константы будет скорее всего доступна только на чтение и при попытке записи вылетит очень некрасивое исключение.
После фразы "Умеет также работать со ссылками" видим работу с указателями :)
reinterpret_cast<whatever>(some *)
reinterpret_cast<integer_expression>(some *)
Итого: За идёю 5, за реализацию 3.
Аналогично забыты звёздочки ещё в куче мест.
И не только звездочки. Вернула все на место.
Кроме того так делать категорически нельзя. Память под строковые константы будет скорее всего доступна только на чтение и при попытке записи вылетит очень некрасивое исключение.
Даже если работать не со строкой, запись в реально константный объект даст undefined behavior, насколько я помню. Но можно снять const, чтобы передать переменную в функцию из не const-корректной библиотеки. При условии, что точно знаешь, что внутри этой самой функции писать никто ничего не будет. Это не самое хорошее решение, но в принципе так сделать можно.
Тут какое дело. То что Undefined будет в любом случае это ясно.
Но если в случае объектов этот номер ещё может пройти, то в случае константных строк они могут (зависит от компилятора и процессора) располагаться в сегменте памяти защищённом от записи на аппаратном уровне.
Кстати, раз уж затронули константность, неплохо бы про mutable написать :)
Кстати, раз уж затронули константность, неплохо бы про mutable написать :)
Дык уже: mutable и const_cast
Есть ещё интересное свойство reinterpret_cast, которое осталось в тени. Нельзя приводить указатель на функцию к указателю на объект:
int foo ();
reinterpret_cast < void* > (foo); //ошибка
По поводу преобразования к (void) - это единственно C-style преобразование, аналога которому нет в C++. Эту операцию можно применять для борьбы с предупреждением о неиспользуемой переменной:
void foo (int i) {
assert (i > 2);
(void) i; // без этой строчки будет warning в оптимизированной версии
}
Боже, это прекрасно, что вы пишете про ЭТО. Конкретно у Вас узнал про "reinterpret_cast" - страшная штука.)
int var = 7;
const int * p3 = new int ( var );
*const_cast < int * > ( p3 ) = 3;
std::cout << *p3 << std::endl;
delete p3; p3 = NULL;
Этот код выполняется нормально. Освобождаем память, присваиваем указателю NULL.
А как сделать к примеру с константой-указателем?
int * const p4 = new int ( var );
delete p4;
const_cast < int* > ( p4 ) = NULL; // <---- Error: lvalue required as left operand of assignment
2Анонимный:
А как сделать к примеру с константой-указателем?
Судя по вопросу, ты скомпилял эти два примера одним компилятором, после чего сделал вывод, что есть разница в приведении типов между const int * p3 и int * const p4.
На самом деле ситуация такая. Результат const_cast для указателей не является lvalue и не может стоять слева в операции присваивания (см. Стандарт 5.2.11 ). Некоторые компиляторы в некоторых случаях это пропускают, но они не правы. Твой компилятор совершенно справедливо ругается на const_cast < int* > ( p4 ) = NULL.
Можно сделать так:
int * p = const_cast < int* > ( p4 ); p = NULL;
Можно сделать так:
const_cast < int*& > ( p4 ) = NULL;
Да, поскольку все переменные изначально определены как const, то результат всех этих действий - undefined behavior. Поскольку ты убираешь const с объекта, который изначально являлся константным, а потом делаешь ему присваивание.
2Алёна:
_Спасибо_, работает.
Начал эту затею чтобы случайно не переписовать адреса указателей в циклах и предотвращать утечку памяти...
Вишло как всегда ))
А мы вышему блогу к экзамену готовимся :)
Спасибо
Cпасибо класный блог, а есть вариант по STL(Контейнерам)
2Анонимный:
Cпасибо класный блог, а есть вариант по STL(Контейнерам)
Ну про вариант не скажу, но кое-что про контейнеры я писала. Поройтесь в постах с тегом cpp.
Спасибо большое, некоторые моменты стали намного понятнее.
Почитал комментарии, и понял, что надо ещё во многом разбираться. За то тоже спасибо =)
А вот такой вопрос! Как самому определить правило для приведения объектов классов?
2Анонимный:
А вот такой вопрос! Как самому определить правило для приведения объектов классов
Я так поняла, нужно вот это: Overloading typecasts
Здравствуйте
Повторю за ост участниками спасибо
Вы начали статью с того, что приведения типов впринципе надо избегать. А как вывести std::string в консоль?
>А как вывести std::string в консоль?
std::string MyString;
...
cout << MyString;
binary '<<' : no operator found which takes a right-hand operand of type 'std::string' (or there is no acceptable conversion)
Возможно, cout << MyString.data();
К простому char* std::string не приводится
2AP:
binary '<<' : no operator found which takes a right-hand operand of type 'std::string' (or there is no acceptable conversion)
Эммм.. #include <iostream> есть? Судя по сообщению об ошибке, << трактуется как бинарный сдвиг.
Я код проверила, прежде чем сюда постить. :-)
К простому char* std::string не приводится
Приводится к const char*, MyString.c_str(). Но оно тут не нужно.
Alena, спасибо вам огромное!
Отправить комментарий