3. Cтиль программирования


Вообще-то стиль это сугубо личное дело каждого программиста, однако при совместной разработке одного проекта программисты должны придерживаться одного, заранее выбранного, стиля. Все, что написано в данном пункте, является всего-лишь примером выбора определенного стиля.

3.1. Имена функций, типов, и т.п.

Наиболее общепринятым сейчас считается такой стиль:

Этот стиль достаточно удобен, если на экране вертикальное расстояние между строками не очень маленькое, иначе он будет раздражать зрение.

Используйте стандартные имена методов:

get...  - получить элемент (пример getX())
set...  - установить элемент (пример setX())
has...  - проверить наличие чего-либо (пример hasLink())
can...  - проверить доступность чего-либо (пример canDraw())
is...   - проверить условие (пример isValid())
do...   - выполнить что-либо (например реагирование на событие)
make... - сконструировать объект (пример makeFlagSet())
to...   - конверсия (пример toRectangle())
virtual Type *clone() - создать копию объекта

3.2. Использование префиксов/суффиксов

С приведенным выше стилем подчеркивание удобно использовать для префиксов и суффиксов. Оно очень наглядно отделяет имя префикса/суффикса от основного идентификатора. Список полезны префиксов и суффиксов:

d_... - член класса (данные).
s_... - статический член класса (скрытые глобальные данные).
m_... - опционально для mutable данных.
..._p - указатель на что-либо.
..._m - функция, изменяющая параметры.
Не стоит пренебрегать использованием префиксов/суффиксов. В исходных текстах программы очень наглядно видно, какая переменная к чему принадлежит.

3.3. Инлайновые функции

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

Не описывайте внутри определения класса инлайновых функций, не помещающихся вместе с объявлением метода в стандартную строку длиной в 80 символов. Вместо этого пишите их ниже, сразу после всех определений классов. Не пишите ключевое слово inline в определении класса.

3.4. Документирование в заголовках

Документирование крайне полезно в больших проектах. Почему ? Ответ очень прост - сгенерированный html-документ содержит перекрестные ссылки. Просмотр и поиск нужных объектов и методов занимает гораздо меньше времени. Вот небольшой пример такой документации в файлах-заголовках: point.h и документация, сгенерированная программой doc++. Этого примера, конечно, недостаточно, чтобы показать действительное преимущество перекрестных ссылок такой системы. Но когда имеется уже 25 классов, то эффект налицо.

3.5. Работа с файлами-заголовками

Общее правилоВключение любого файла заголовка, в любом порядке в модуль не должно приводить к сообщениям об ошибке при компиляции.
Пример избежания ошибок при повторном включении заголовка, и хороший пример подключения необходимых заголовков - внутреннего somefile1.h и стандартного string.h.

// ------- string.h (системный, другой стиль ifndef/endif):
#ifndef __STRING_H__
#define __STRING_H__
.....
#endif
// ------- somefile1.h:
#ifndef I_SOMEFILE1_H
#define I_SOMEFILE1_H
....
#endif
// ------- somefile.h:
#ifndef I_SOMEFILE_H
#define I_SOMEFILE_H

// обратите внимание на дополнительное ifndef/endif обрамление
#ifndef I_SOMEFILE1_H
#include "somefile1.h"
#endif

#ifndef I_STRING_H
#include <string.h>
#define I_STRING_H
#endif

#endif
Хотя повторное включение файлов заголовков с такой "охраной" и не приводит к ошибке, оно сильно замедляет скорость компиляции, т.к. препроцессор читает ВЕСЬ файл и соответственно обрабатывает большой пустой блок. Это незаметно, когда количество+размер файлов-заголовков невелик. Однако представим ситуацию. Имеются заголовки "s.h", "s1.h ... sN.h", "w1.h ... wN.h". Каждый si.h включает в себя все w*.h. Заголовок s.h включает в себя все s*.h. При N > 16 скорость компиляции без дополнительных проверок ifndef/endif заметно ниже, чем с дополнительными ifndef/endif. Дело в том, что количество обработанных заголовков уменьшилось с N2+1 до 2*N+1. При большом N это очень выгодно. В самом модуле обрамление include в ifndef/endif лучше не делать, это портит вид шапки модуля. Не стоит писать include в середине модуля.

3.6. Процедурный код

Отступление от левой границы должно всегда измеряться табуляциями, а не пробелами. К сожалению в данном FAQ'е были использованы пробелы, потому что не все браузеры позволяют уменьшить шаг табуляции.

После встроенных ключевых слов if, while, for ставьте пробел перед скобкой, это визуально отделяет ключевые слова от функций. Пример:

if (some_condition)
for (init1,init2;cond;iter)
somefunc(a,b,c);

Пробелы в выражениях.Есть две хорошие методики - ставить пробелы после каждой лексеммы и не ставить пробелы вообще. Строки с использованием первой методики будут значительно длиннее, но читабельность естественно выше. Использовать другие расстановки пробелов - дело невыгодное. Программист начнет писать программу медленнее, если он будет дополнительно задумываться "ставить пробел или нет ?". Однако если у программистов есть один общий наработанный стиль расстановки пробелов - лучше использовать его.

for ( int i = 0 ; i < 15 ; ++ i ) ...
for (int i=0;i<15;++i) ...

{ и }. Фигурные скобки удобно ставить одной вертикальной линией. K&R стиль себя не оправдывает, блоки плохо видно визуально если ширина табуляции меньше 8. K&R стиль расстановки фигурных скобок можно эффективно использовать только при размере табуляции не меньше 8. BSD стиль, ничем не отличается по читабельности, от предлагаемого, но не позволяет использование табуляции для формирования левой границы, т.к. скобки находятся на половинных делениях.

Предлагаемый стиль:
....
    if (something)
    {
        ....
    }
    else
    {
        ....
    }
....

K&R стиль:
....
    if (something) {
        ....
    } else {
        ....
    }
....

BSD стиль:
....
    if (something)
      {
        ....
      }
    else
      {
        ....
      }
....

Локальные переменные.Не называйте локальные переменные именами длиннее 7-8 символов. Это неудобно. Также следите за количеством локальных переменных, средний программист, как правило, не может уследить больше чем за 7-9 локальными переменными.

void f()
{
    int thisIsBadNameForLocalVariable;
    int good;
}

Комментарии и избыточное комментирование.Никогда не комментируйте КАК ваш код работает. Лучше опишите ЧТО он делает. При избыточном комментировании код становится тяжелее читать, чем код вообще без комментариев. Пример этому есть очень хороший - исходные тексты большинства программ MicroSoft'а. (есть хороший пример прогона их файла через препроцессор, когда 30kb исходника превратилось в 6kb).
Бесполезный комментарий:

....
// присвоим ноль переменной a
a = 0;
....
Полезный комментарий:
....
// установим начальное состояние конечному автомату
a = 0;
....


Валерий Щедрин <valery@forthpick.kiev.ua>

1