понятным.

Чрезмерное применение понижающего приведения может служить признаком слишком бедного интерфейса базового класса. Такой интерфейс может привести к тому, что большая функциональность определяется в производных классах, и всякий раз при необходимости расширения интерфейса приходится использовать понижающее приведение. Одно из решений данной проблемы — перепроектирование базового интерфейса в целях повышения функциональности.

Тогда и только тогда, когда становятся существенны накладные расходы, вызванные применением 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

94. Избегайте преобразований, отменяющих const

Резюме

Преобразование типов, отменяющее const, может привести к неопределенному поведению, а кроме того, это свидетельство плохого стиля программирования даже в том случае, когда применение такого преобразования вполне законно.

Обсуждение

Применение const — это дорога с односторонним движением, и, воспользовавшись этим спецификатором, вы не должны давать задний ход. Если вы отменяете const для объекта, который изначально был объявлен как константный, задний ход приводит вас на территорию неопределенного поведения. Например, компилятор может (и, бывает, так и поступает) поместить константные данные в память только для чтения (ROM) или в страницы памяти, защищенные от записи. Отказ от const у такого истинно константного объекта — преступный обман, зачастую караемый аварийным завершением программы из-за нарушения защиты памяти.

Даже если ваша программа не потерпит крах, отмена const представляет собой отмену обещанного и не делает того, чего от нее зачастую ожидают. Например, в приведенном фрагменте не происходит выделения массива переменной длины:

void Foolish(unsigned int n) {

 const unsigned int size = 1;

 const_cast<unsigned int&>(size) = n; // Не делайте так!

 char buffer[size];                   // Размер массива

 // ...                               // все равно равен 1

}

В С++ имеется одно неявное преобразование const_cast из строкового литерала в char*:

char* weird = 'Trick or treat?';

Компилятор молча выполняет преобразование const_cast из const char[16] в char*. Это преобразование позволено для совместимости с API в стиле С, хотя и представляет собой дыру в системе типов С++. Строковые литералы могут размещаться в памяти только для чтения, и попытка изменения такой строки может вызвать нарушение защиты памяти.

Исключения

Преобразование, отменяющее const, может оказаться необходимым для вызова функции API, некорректно указывающей константность (см. рекомендацию 15). Оно также полезно, когда функция, которая должна получать и возвращать ссылку одного и того же типа, имеет как константную, так и неконстантную перегрузки, причем одна из них вызывает другую:

const Object& f(const Object&);

Object& f(Object& obj {

 const Object& ref = obj;

 return const_cast<Object&>(f(ref)); // преобразование

}                                    // возвращаемого типа

Ссылки

[Dewhurst03] §32, §40 • [Sutter00] §44

95. Не используйте преобразование типов в стиле С

Резюме

Возраст не всегда означает мудрость. Старое преобразование типов в стиле С имеет различную (и часто опасную) семантику в зависимости от контекста, спрятанную за единым синтаксисом. Замена преобразования типов в стиле С преобразованиями С++ поможет защититься от неожиданных ошибок.

Обсуждение

Одна из проблем, связанных с преобразованием типов в стиле С, заключается в том, что оно использует один и тот же синтаксис для выполнения несколько разных вещей, в зависимости от таких мелочей, как, например, какие именно заголовочные файлы включены при помощи директивы #include. Преобразования типов в стиле С++, сохраняя определенную опасность, присущую преобразованиям вообще, имеют четко документированное предназначение, их легко найти, дольше писать (что дает время дважды подумать при их использовании), и не позволяют незаметно выполнить опасное преобразование reinterpret_cast (см. рекомендацию 92).

Рассмотрим следующий код, в котором Derived — производный от базового класса Base:

extern void Fun(Derived*);

void Gun(Base* pb) {

// Будем считать, что функция Gun знает, что pb в

// действительности указывает на объект типа Derived и

Добавить отзыв
ВСЕ ОТЗЫВЫ О КНИГЕ В ИЗБРАННОЕ

0

Вы можете отметить интересные вам фрагменты текста, которые будут доступны по уникальной ссылке в адресной строке браузера.

Отметить Добавить цитату