CHAP2-4 (1018803), страница 5
Текст из файла (страница 5)
++first_nonwhite;
// В этой ситуации имя *first_nonwhite говорит вам гораздо
// больше о том, что делает переменная, чем предыдущее "*pstr".
45. Имена макросов должны записываться ЗАГЛАВНЫМИ_БУКВАМИ.
Как показывается в последующих разделах, макросы часто вызывают побочные эффекты. Поэтому полезно иметь возможность определить с первого взгляда, что у вас является макросом. Конечно, вы не должны использовать только заглавные буквы для чего-нибудь помимо макросов, иначе вы не достигнете цели данного правила.
45.1. Не используйте заглавных букв для констант перечисления.
Должна быть обеспечена возможность замены констант, определенных в перечислении, на переменную типа const. Если ее имя записано заглавными буквами, то вам придется его менять. Кроме того, имеются еще и проблемы с макросами (вскоре будут рассмотрены), которых нет у перечислений. Поэтому будет полезно иметь возможность различать их с первого взгляда.
45.2. Не используйте заглавных букв в именах типов, созданных при помощи typedef.
Так как макрос также может использоваться в манере, подобной typedef, то полезно знать может или нет что-то быть использовано в качестве синтаксически правильного типа. Например, имея:
typedef void (*ptr_to_funct)(int );
вы можете написать следующее:
(ptr_to_funct)( p ); // преобразует p в указатель на функцию
ptr_to_funct f(long); // f возвращает указатель на функцию
Макрос типа:
#define PTR_TO_FUNCTION void (*) (int )
позволяет вам сделать преобразование:
(PTR_TO_FUNCTION) ( p );
но не позволяет объявить функцию:
PTR_TO_FUNCTION f(long);
Указанный макрос при подстановке дает:
void (*) (int ) f(long);
но компилятору нужно:
void (*f(long))(int );
Имя типа из строчных букв не вызовет никаких проблем при чтении, потому что по смыслу вы всегда можете сказать, используется ли оно для типа или нет.
46. Избегайте области имен стандарта ANSI C.
Идентификаторы, начинающиеся с символа подчеркивания, и имена типов, оканчивающиеся на _t, были зарезервированы стандартом ANSI C для использования разработчиками компиляторов. Не используйте эти символы. Также избегайте имен функций, вошедших в стандарт ANSI C и в проект стандарта ISO/ANSI для С++.
47. Избегайте области имен Microsoft.
Это может показаться правилом, специфичным только для Microsoft, но на самом деле это не так (учитывая имеющуюся склонность Microsoft к мировому господству). Любой, кто заботится о переносимости, должен рассчитывать на то, что его или ее программа со временем может или работать под управлением операционной системы Microsoft, или взаимодействовать с библиотекой классов Microsoft. Библиотека MFC, например, перенесена на Macintosh и во многие операционные среды UNIX/Motif на момент написания этой книги, и, вероятно, появится на других операционных системах в ближайшем будущем.
На момент написания этой книги интерфейс прикладного программирования Windows (API) включает в себя около 1200 функций. Библиотека MFC, быстро вытесняющая первоначальный интерфейс на языке С, добавляет около 80 определений классов. К сожалению, метод Microsoft состоит в добавлении каждый раз дополнительных функций и классов в новую версию компилятора. Если Microsoft случайно выберет в качестве имени для функции или класса то, которое вы используете для каких-то других целей, угадайте, кому из вас придется его сменить?
Так как ни один из идентификаторов Microsoft не соответствует стандарту ANSI C, требующему, чтобы имена поставляемых разработчиком объектов начинались с символа подчеркивания, то вы должны предохраняться, избегая использования соглашений по выбору имен в стиле Microsoft:
-
Все имена функций Microsoft используют соглашения в стиле Паскаля о СмесиЗаглавныхИСтрочныхБукв(), и они всегда начинаются с заглавной буквы. Я предпочитаю имена только из строчных букв с символами подчеркивания, но что бы вы не выбрали, НеИспользуйтеСтильMicrosoft(). Функции-члены в классах MFC используют то же самое соглашение.
-
Все имена классов Microsoft начинаются с заглавной "С" с последующей заглавной буквой (например, CString, CWnd, CDialog и т.д.). Начальная С мало что дает, кроме беспорядка, и ее пропуск удаляет нас от области имен Microsoft.
-
Одна из наиболее фундаментальных заповедей объектно-ориентированного проектирования запрещает оставлять незащищенными данные-члены в определении класса. Тем не менее, многие классы MFC имеют открытые поля данных. Все эти поля начинаются с m_, не имеющих другого назначения, кроме как увеличить беспорядок. Тем не менее, мы можем использовать эту бессмыслицу для того, чтобы не начинать имена своих собственных полей с m_ и таким образом легко отличать свои члены от унаследованных из базовых классов MFC.
48. Избегайте ненужных идентификаторов.
Имена для констант часто вообще не нужны. Например, не определяйте значения, возвращаемые при ошибке; если возвращается всего одна ошибка, возвратите просто FALSE. Не делайте так:
enum { INSERT_ERROR, DELETE_ERROR };
insert()
{
//...
return INSERT_ERROR;
}
delete()
{
//...
return DELETE_ERROR;
}
а просто возвратите 0 в случае ошибки и в случае успеха любое правильное значение типа 1.
49. Именованные константы для булевых величин редко необходимы.
Выбор неверного имени может добавить значительную ненужную сложность в вашу программу. Рассмотрим следующую простейшую функцию, которая подсчитывает количество слов в строке:
int nwords(const char *str)
{
typedef enum { IN_WORD, BETWEEN_WORDS } wstate;
int word_count = 0;
wstate state = BETWEEN_WORDS;
for (; *str ; ++str )
{
if ( isspace(*str) )
state = BETWEEN_WORDS;
else
if ( state != IN_WORD )
{
++word_count;
state = IN_WORD;
}
}
return word_count;
}
Неправильно выбранное имя state заставило нас ввести два ненужных идентификатора: IN_WORD и BETWEEN_WORDS. Теперь взгляните на этот вариант:
int nwords2(const char *str)
{
int word_count = 0;
int in_word = 0;
for (; *str ; ++str )
{
if ( isspace(*str) )
in_word = 0;
else
if ( !in_word )
{
++word_count;
in_word = 1;
}
}
return word_count;
}
Переименование нечетко названной переменной state во что-нибудь, что действительно описывает назначение переменной, позволило мне исключить булевые именованные константы IN_WORDS и BETWEEN_WORDS. Получившаяся подпрограмма меньше и легче читается.
Вот другой пример. Следующая программа:
enum child_type { I_AM_A_LEFT_CHILD, I_AM_A_RIGHT_CHILD };
struct tnode
{
child_type position;
struct tnode *left,
*right;
} t;
//...
t.position = I_AM_LEFT_CHILD;
if ( t.position == I_AM_LEFT_CHILD )
//...
может быть упрощена подобным образом:
struct tnode
{
unsigned is_left_child ;
struct tnode *left,
*right;
} t;
t.is_left_child = 1;
if ( t.is_left_child )
//...
тем самым исключая два ненужных идентификатора. И вот последний пример:
enum { SOME_BEHAVIOR, SOME_OTHER_BEHAVIOR, SOME_THIRD_BEHAVIOR };
f( SOME_BEHAVIOR , x);
f( SOME_OTHER_BEHAVIOR, x);
f( SOME_THIRD_BEHAVIOR, x);
требующий четырех идентификаторов (три именованные константы и имя функции). Лучше, хотя это не всегда возможно, исключить селекторную константу в пользу дополнительных функций:
some_behavior(x);
some_other_behavior(x);
some_third_behavior(x);
Обратной стороной этой монеты является вызов функции. Рассмотрим следующий прототип:
create_window( int has_border, int is_scrollable, int is_maximized );
Я снова выбрал рациональные имена для исключения необходимости в именованных константах. К сожалению, вызов этой функции плохо читаем:
create_window( TRUE, FALSE, TRUE );
Просто взглянув на такой вызов, я не получу никакого представления о том, как будет выглядеть это окно. Несколько именованных констант проясняют обстоятельства в этом вызове:
enum { UNBORDERED =0; BORDERED =1}; // Нужно показать значения
enum { UNSCROLLABLE=0; SCROLLABLE =1}; // или create_window()
enum { NORMAL_SIZE =0; MAXIMIZED =1}; // не будет работать.
//...
create_window( BORDERED, UNSCROLLABLE, MAXIMIZED );
но теперь у меня другая проблема. Я не хочу использовать именованные константы внутри самой create_window(). Они здесь только для того, чтобы сделать ее вызов более читаемым, и я не хочу загромождать эту функцию таким кодом, как:
if ( has_border == BORDERED )
//...
сравнивая его с более простым:
if ( has_border )
//...
Первый вариант уродлив и многословен. К сожалению, если кто-то изменит значение именованной константы BORDERED, второй оператор if не будет работать. Я обычно соглашаюсь с мнением, что программист, занимающийся сопровождением, не должен менять значения идентификаторов, как я это проделал в предыдущем примере.
1 Web описана в книге Дональда Кнута The WEB System of Structured Documentation (Palo Alto: Stanford University Dept. of Computer Science, Report No.STAN-CS-83-980, 1983). Система CWeb описана в книге Дональда Е. Кнута и Сильвио Ливая The CWeb System of Structured Documentation (Reading: Addison Wesley, 1994). Обе публикации не только описывают как работают эти системы, но хорошо демонстрируют это. В этих книгах документируются реальные тексты программ, реализующих указанные системы.
TeX является редакционно-издательской системой Кнута. Она имеется в нескольких коммерческих версиях.
* "До каких же пор ты, Катилина, будешь испытывать наше терпение...”.
2 Я подозреваю, что венгерская запись так интенсивно используется вследствие того, что большая часть Microsoft Windows написана на языке ассемблера.
3 По крайней мере оно должно быть. Я подозреваю, что некоторые энтузиасты венгерской записи так плохо организуют свои программы, что просто не могут найти нужные объявления. Включая тип в имя, они избавляются от многих часов поисков в неудачно спроектированных листингах. Программы на языке ассемблера, которые по необходимости включают в себя множество глобальных переменных, являются очевидным исключением.















