Как гласит римская пословица, у лжи короткие ноги. Не пытайтесь использовать reinterpret_cast
, чтобы заставить компилятор рассматривать биты объекта одного типа как биты объекта другого типа. Такое действие противоречит безопасности типов.
Вспомните:
Преобразование reinterpret_cast
отражает представления программиста о представлении объектов в памяти, т.е. программист берет на себя ответственность за то, что он лучше компилятора знает, что можно и что нельзя. Компилятор молча сделает то, что вы ему скажете, но применять такую грубую силу в отношениях с компилятором — последнее дело. Избегайте каких-либо предположений о представлении данных, поскольку такие предположения очень сильно влияют на безопасность и надежность вашего кода.
Кроме того, реальность такова, что результат применения reinterpret_cast
еще хуже, чем просто насильственная интерпретация битов объекта (что само по себе достаточно нехорошо). За исключением некоторых гарантированно обратимых преобразований результат работы reinterpret_cast
зависит от реализации, так что вы даже не знаете точно, как именно он будет работать. Это очень ненадежное и непереносимое преобразование.
Некоторые низкоуровневые специфичные для данной системы программы могут заставить вас применить reinterpret_cast
к потоку битов, проходящих через некоторый порт, или для преобразования целых чисел в адреса. Используйте такое небезопасное преобразование как можно реже и только в тщательно скрытых за абстракциями функциях, чтобы ваш код можно было переносить с минимальными изменениями. Если вам требуется преобразование между указателями несвязанных типов, лучше выполнять его через приведение к void*
вместо непосредственного использования reinterpret_cast
, т.е. вместо кода
T1* p1 = ... ;
T2* p2 = reinterpret_cast
лучше писать
T1* p1 = ...;
void* pV = p1;
T2* p2 = static_cast
93. Избегайте применения static_cast
к указателям
К указателям на динамические объекты не следует применять преобразование static_cast
. Используйте безопасные альтернативы — от dynamic_cast
до перепроектирования.
Подумайте о замене static_cast
более мощным оператором dynamic_cast
, и вам не придется запоминать, в каких случаях применение static_cast
безопасно, а в каких — чревато неприятностями. Хотя dynamic_cast может оказаться немного менее эффективным преобразованием, его применение позволяет обнаружить неверные преобразования типов (но не забывайте о рекомендации 8). Использование static_cast
вместо dynamic_cast
напоминает экономию на ночном освещении, когда выигрыш доллара в год оборачивается переломанными ногами.
При проектировании постарайтесь избегать понижающего приведения. Перепроектируйте ваш код таким образом, чтобы такое приведение стало излишним. Если вы видите, что передаете в функцию базовый класс там, где в действительности потребуется производный класс, проследите всю цепочку вызовов, чтобы понять, где же оказалась потерянной информация о типе; зачастую изменение пары прототипов оказывается замечательным решением, которое к тому же делает код более простым и понятным.
Чрезмерное применение понижающего приведения может служить признаком слишком бедного интерфейса базового класса. Такой интерфейс может привести к тому, что большая функциональность определяется в производных классах, и всякий раз при необходимости расширения интерфейса приходится использовать понижающее приведение. Одно из решений данной проблемы — перепроектирование базового интерфейса в целях повышения функциональности.
Тогда и только тогда, когда становятся существенны накладные расходы, вызванные применением dynamic_cast
(см. рекомендацию 8), следует подумать о разработке собственного преобразования типов, который использует dynamic_cast
при отладке и static_cast
в окончательной версии (см. [Stroustrup00]):
template
assert(dynamic_cast
static_cast
return static_cast
}
template
typedef tr1::remove_reference
assert(dynamic_cast
static_cast
return static_cast