Правила программирования на Си и Си++


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 и размера при объявлении массива. Ни в одной из указанных ситуаций константа использоваться не может§.



Содержание раздела