Использование указателя pc
для инициализации объекта типа string
— хороший пример небезопасности оператора reinterpret_cast
. Проблема в том, что при изменении типа компилятор не выдаст никаких предупреждений или сообщений об ошибке. При инициализации указателя pc
адресом типа int
компилятор не выдаст ни предупреждения, ни сообщения об ошибке, поскольку явно указано, что это и нужно. Однако любое последующее применение указателя pc
подразумевает, что он содержит адрес значения типа char*
. Компилятор не способен выяснить, что фактически это указатель на тип int
. Таким образом, инициализация строки str
при помощи указателя pc
вполне правомерна, хотя в данном случае абсолютно бессмысленна, если не хуже! Отследить причину такой проблемы иногда чрезвычайно трудно, особенно если приведение указателя ip
к pc
происходит в одном файле, а использование указателя pc
для инициализации объекта класса string
— в другом.
reinterpret_cast
жестко зависит от конкретной машины. Чтобы безопасно использовать оператор reinterpret_cast
, следует хорошо понимать, как именно реализованы используемые типы, а также то, как компилятор осуществляет приведение.
В ранних версиях языка С++ явное приведение имело одну из следующих двух форм:
(
В зависимости от используемых типов, приведение старого стиля срабатывает аналогично операторам const_cast
, static_cast
или reinterpret_cast
. В случаях, где используются операторы static_cast
или const_cast
, приведение типов в старом стиле позволяет осуществить аналогичное преобразование, что и соответствующий именованный оператор приведения. Но если ни один из подходов не допустим, то приведение старого стиля срабатывает аналогично оператору reinterpret_cast
. Например, используя форму записи старого стиля, можно получить тот же результат, что и с использованием reinterpret_cast
.
char *pc = (char*) ip; //
Приведение нарушает обычный порядок контроля соответствия типов (см. раздел 2.2), поэтому авторы настоятельно рекомендуют избегать приведения типов. Это особенно справедливо для оператора reinterpret_cast
. Такие приведения всегда опасны. Операторы const_cast
могут быть весьма полезны в контексте перегруженных функций, рассматриваемых в разделе 6.4. Использование оператора const_cast
зачастую свидетельствует о плохом проекте. Другие операторы приведения, static_cast
и dynamic_cast
, должны быть необходимы нечасто. При каждом применении приведения имеет смысл хорошо подумать, а нельзя ли получить тот же результат другим способом. Если приведение все же неизбежно, имеет смысл принять меры, позволяющие снизить вероятность возникновения ошибки, т.е. ограничить область видимости, в которой используется приведенное значение, а также хорошо документировать все подобные случаи.
Упражнение 4.36. С учетом того, что i
имеет тип int
, a d
— double
, напишите выражение i *= d
так, чтобы осуществлялось целочисленное умножение, а не с плавающей запятой.
Упражнение 4.37. Перепишите каждое из следующих приведений старого стиля так, чтобы использовался именованный оператор приведения.
int i; double d; const string *ps; char *pc; void *pv;
(a) pv = (void*)ps; (b) i = int(*pc);
(c) pv = &d (d) pc = (char*)pv;
Упражнение 4.38. Объясните следующее выражение: