|
Программирование >> Решение нетривиальных задач
что расширяется макросом до: y + 1 * y + 1 и вычисляется как: y + (1 * y) + 1 И вновь круглые скобки приходят на помощь. Следующее определение: #define SQUARE(x) ((x) * (x)) расширяется до: ((y + 1) * (y + 1)) 82. enum и const лучше, чем макрос Директива #define должна быть вашим последним средством при определении значения константы. Рассмотрим следующую рассмотренную ранее распространенную ошибку: #define TWO K 1024 + 1024 x = TWO K * 10 что в результате вычисления дает 11264 (1024+(1024*10)) вместо требуемых 20480. Определение перечисления типа: enum { two k = 1024 + 1024 }; или константы типа: const int Two k = 1024 + 1024; не вызывает трудностей, связанных с макросом. И круглые скобки не требуются. Перечисление enum на несколько очков превосходит константу: во-первых, определение const int в языке Си на самом деле выделяет память под тип int и инициализирует ее. Вы не можете модифицировать эту область памяти, но память при этом занята. Следовательно, определение константы в Си нельзя поместить в заголовочном файле; вы нужно будет воспользоваться модификатором extern как для какой-нибудь глобальной переменной. (В Си++ все это несущественно, так как там память выделяется лишь тогда, когда вы определяете адрес константы или передаете его по ссылке. Определения констант в Си++ могут - а на деле часто и должны - помещаться в заголовочном файле). Перечисление отличается тем, что память для него никогда не выделяется. Подобно макросу, оно может вычисляться во время компиляции. Следовательно, при использовании вами перечисления не * С этим утверждением автора, так и следующим за ним примером инкрементирования аргумента макроса нельзя согласиться. - Ред. происходит потери производительности. Второй проблемой является порча области глобальных имен. Область действия перечисления легко ограничивается. Например, в следующем фрагменте кода перечисление default i действует лишь внутри функции f() : void f( int i ) enum { default i = 1024 }; if ( !i ) i = default i ; В фрагменте: void f( int i ) #define DEFAULT I 1024 if ( !i ) i = DEFAULT I ; макрос DEFAULT I виден всем функциям, чьи определения следуют после определения этого макроса. Если DEFAULT I определяется в заголовочном файле, то он будет виден в нескольких файлах - даже если он не используется кодом в этих файлах. Та же самая проблема касается также константы, определенной на глобальном уровне. Перечислитель enum особенно полезен в Си++, потому что он может быть ограничен областью действия класса и инициализироваться в самом определении класса вместо конструктора. Эти вопросы рассматриваются далее в той части книги, что посвящена правилам Си++. Наконец, перечислитель может быть использован в качестве аргумента оператора case и размера при объявлении массива. Ни в одной из указанных ситуаций константа использоваться не может*. 83. Аргумент параметризированного макроса не должен появляться в правой части более одного раза Макрос SQUARE() даже в своем модифицированном виде представил выше серьезную проблему. Дано: #define SQUARE(x) ((x)*(x)) Выражение SQUARE(++x) дважды инкрементирует x. После чего макрос в этом случае дает неверный результат. Если x вначале содержит 2, то SQUARE(++x) вычисляется как 3*4. Такое поведение есть пример побочного эффекта макроса - ситуации, когда макрос ведет себя неожиданно. SQUARE(++x) также показывает пример ситуации, в которой использование макроса просто слишком рискованно для оправдания сложностей сопровождения. Встроенная функция Си++ или шаблон, расширяемый до встроенной функции, являются более удачными решениями. Даже в Си простую функцию с неудачными аргументами легче сопровождать, чем эквивалентный макрос: double square( double x ) return x * x; Но, тем не менее, у меня есть серьезное сомнение в том, что использование функции для скрытия простого умножения является стоящим делом. 83.1. Никогда не используйте макросы для символьных констант Например: #define SPACE имеет смысл, если только вы намерены использовать вместо пробела другой символ (как если бы вы испытывали, например, программу для замены символов табуляции). Никогда не делайте так: #define SPACE 0x20 Действительное значение символьной константы для пробела ( ) изменяется компилятором так, чтобы оно соответствовало операционной среде, для которой ведется компиляция. Для среды, поддерживающей ASCII, это значение будет 0x2 0, а для EBDCDIC - уже нечто другое. Не думайте, что у какого-то символа свое постоянное значение.
|
© 2006 - 2024 pmbk.ru. Генерация страницы: 0
При копировании материалов приветствуются ссылки. |