93. Избегайте применения 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<class To, class From> To checked_cast(From* from) {
assert(dynamic_cast<To>(from) ==
static_cast<To>(from) && "checked_cast failed" );
return static_cast<To>(from);
}
template<class To, class From> To checked_cast(From& from) {
typedef tr1::remove_reference<To>::type* ToPtr; // [C++TR104]
assert(dynamic_cast<ToPtr>(&from) ==
static_cast<ToPtr>(&from) && "checked_cast failed");
return static_cast<To>(from);
}
Эта пара функций (по одной для указателей и ссылок) просто проверяет согласованность двух вариантов преобразования. Вы можете либо самостоятельно приспособить checked_cast для своих нужд, либо использовать имеющийся в библиотеке.
Ссылки
[Dewhurst03] §29, §35, §41 • [Meyers97] §39 • [Stroustrup00] §13.6.2 • [Sutter00] §44