Здравствуйте Libra, Вы писали: L>Привет всем. Недавно для себя обнаружил некий оператор приведения типов L> L>
L>reinterpret_cast <BYTE*> ( variable );
L>
L>И сразу возник вопрос чем он отличается от обычного приведения типов? L>например L>
L>(unsigned char*)variable;
L>
L>Заранее благодарен.
В С++ введены следующие операторыприведения типов взамен приведения в стиле языка С.
Основная идея — сделать приведение типов более наглядным и понятным.
Дело в том, что при приведении типов в стиле С, т.е. например
(CClassA*) pObj;
не вполне понятно, что имеется ввиду. То ли происходит приведение указателя на потомок к указателю на базовый класс, то ли pObj — константный объект, и есть необходимость привести его к тому же классу, но не константному... То ли pObj указывает на массив байтов, которые нужно интерпретировать как объект класса CClassA... В каждом из случаев своя логика, свой смысл, свое назначение.
Для того, чтобы более четко выделить назначение и смысл приведения типов, в язык введены следующие операторы приведения типов
Описание каждого из них несколько объемное, так что рекомендую посмотреть в книге, например, Страуструпа "Язык программирования С++", и задать вопросы (если возникнут).
Здравствуйте Libra, Вы писали:
L>Привет всем. Недавно для себя обнаружил некий оператор приведения типов L> L>
L>reinterpret_cast <BYTE*> ( variable );
L>
L>И сразу возник вопрос чем он отличается от обычного приведения типов? L>например L>
L>(unsigned char*)variable;
L>
L>Заранее благодарен.
Страуструп, 6.2.7
"... Оператор static_cast осуществляет преобразование родственных типов, ...
Оператор reinterpret_cast управляет преобразованиями между несвязанными типами, например целых в указатели ...
Такое различие позволяет компилятору осуществлять минимальную проверку типов при static_cast, а программисту — легче обнаружить опасные преобразования, представляемые reinterpret_cast. Некоторые преобазования static_cast являются переносимыми, но reinterpret_cast практически никогда не переносимо. Хотя никаких гарантий относительно reinterpret_cast дать нельзя, в результате обычно получается значение нового типа, состоящее из той же цепочки битов, что и аргумент преобразования. ...
Также имеется преобразование dynamic_cast, выполняемое и проверяемое на этапе выполнения, и const_cast, которое аннулирует действие модификатора const.
C++ унаследовал от С форму записи (T)e, означающую любое преобразование, ... для получения типа T из выражения e. Такой стиль намного опаснее, чем именованные операторы преобразования, потому что приведенную форму записи труднее отслеживать в большой программе и вид преобразования ... не очевиден. ... не зная конкретно типы T и e, невозможно сказать, что именно делает (T)e."
Вот так. То, что раньше делалось в С с помошью приведения типов T(e), в С++ стало гораздо сложнее по причине появления объектов, которых не было в С (например, указатели на функции-члены, множественное насдледование), и типы преобразований между голыми данными, принятые в С, были разделены на типы преобразований с разделением обязанностей. Теперь, используя один из перечисленных операторов, можно точно указать как компилятору, так и программисту, какой именно тип преобразования ожидается, и отслеживать возможные ошибки стало легче.
Здравствуйте ynblpb, Вы писали:
Y>Если это кусочек из книги в электронном варианте.. подскажите где её можно достать... так сказать для просвящения... ЗЫ: заранее благодарен.
Есть на www.webdoc.ru, но только текст, без ссылок, без содержания одним куском (370К — жуть). Я брал из печатного варианта, а где есть нормальный электронный — не знаю.
Ко всему сказанному хотел бы вот что добавить.
В отличие от static_cast, на результат работы reinterpret_cast нельзя полагаться ни в одном из случаев за исключением приведения к исходному типу. Все остальные применения в лучшем случае непереносимы. Вот почему преобразования между указателем на функцию и указателем на член относятся к reinterpret_cast, а не static_cast. Например:
void thump(char* p) { *p = 'x'; }
typedef void (*PF)(const char*);
PF pf;
void g(const char* pc)
{
thump(pc); // ошибка: неправильный тип аргумента
pf = &thump; // ошибка
pf = static_cast<PF>(&thump); // ошибка!
pf = reinterpret_cast<PF>(&thump); // допускается, но за последтсвия отвечаем только мы
pf(pc); // правильная работа не гарантируется!
}
Присваивать pf указатель на thump опасно, т.к. это действие в обход системы типов. С помощью данной операции адрес константы может быть передан какой-то функции, которая сделает попытку ее модифицировать. Поэтому и нужно использовать приведение, причем именно оператор reinterpret_cast.
Оператор reinterpret_cast не осуществляет навигацию по иерархии классов. Например:
class A { /* ... */ };
class B { /* ... */ };
class D : public A, public И { /* ... */ };
void f(B* pb)
{
D* pd1 = reinterpret_cast<D*>(pb);
D* pd2 = static_cast<D*>(pb);
}
Здесь pd1 и pd2, как правило, получат различные значения.При вызове
f(new D);
pd2 будет указывать на начало переданного объекта D, а pd1 — на начало подобъекта B в объекте D.
Как говорит Страуструп: "Операция reinterpret_cast<T>(arg) почти так же неудачна, как и (T)arg."