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