Как стать автором
Обновить

Руководство Google по стилю в C++. Часть 9

Время на прочтение8 мин
Количество просмотров9.7K
Автор оригинала: Google
Часть 1. Вступление

Часть 8. Именование
Часть 9. Комментарии
Часть 10. Форматирование



Эта статья является переводом части руководства Google по стилю в C++ на русский язык.
Исходная статья (fork на github), обновляемый перевод.

Комментарии


Комментарии являются обязательными для кода (если вы планируете его читать). Следующие правила описывают, что вы должны комментировать и как. Но помните: хотя комментарии очень важны, идеальный код сам себя документирует. Использование «говорящих» имён для типов и переменных намного лучше, чем непонятные имена, которые потом требуется расписывать в комментариях.

Комментируйте код с учётом его следующих читателей: программистов, которым потребуется разбираться в вашем коде. Учтите, что следующим читателем можете стать вы!

Стиль комментариев


Используйте либо // либо /* */, пока не нарушается единообразие.

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

Комментарии в шапке файла


В начало каждого файла вставляйте шапку с лицензией.

Комментарии в файле должны описывать его содержимое. Если файл объявляет, описывает или тестирует одну абстракцию (на которую уже есть комментарий), дополнительное описание в шапке файла не нужно. В ином случае, в начало файла вставляйте описание содержимого.

Правовая информация и список авторов


Каждый файл должен содержать информацию о лицензии. Формат описания зависит от лицензии, используемой в проекте. У каждой лицензии (Apache 2.0,BSD, LGPL, GPL, др.) могут быть свои требования к оформлению.

Если вы делаете значительные изменения в файле, подумайте над удалением прежнего списка авторов. Обновлённые файлы могут уже не содержать упоминание об авторских правах и список авторов.

Содержимое файлов


Если .h объявляет несколько абстракций, комментарий в шапке файла должен в целом описывать содержимое файла и как абстракции связаны друг с другом. Одного, двух предложений в комментарии обычно достаточно. Более детальная информация расписывается в другом месте (не в шапке файла).

Не дублируйте комментарии в .h и .cc файлах — со временем комментарии становятся разными.

Комментарии класса


Каждое объявление класса (кроме совсем очевидных) должно сопровождаться комментарием, для чего класс и как им пользоваться.

// Перебор содержимого GargantuanTable.
// Пример:
//    GargantuanTableIterator* iter = table->NewIterator();
//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
//      process(iter->key(), iter->value());
//    }
//    delete iter;
class GargantuanTableIterator {
  ...
};

Комментарий к классу должен быть достаточным для понимания: как и когда использовать класс, дополнительные требования для правильного использования класса. Описывайте, если требуется, ограничения (предположения) на синхронизацию в классе. Если экземпляр класса может использоваться из разных потоков, обязательно распишите правила многопоточного использования.

В комментарии к классу также можно привести короткие примеры кода, показывающие как проще использовать класс.

Обычно класс объявляется/определяется в разных файлах (.h и .cc). Комментарии, описывающие использование класса должны быть рядом с определением интерфейса. Комментарии о тонкостях реализации должны быть рядом с кодом самих методов.

Комментарии функции


Комментарии к объявлению функции должны описывать использование функции (кроме самых очевидных случаев). Комментарии к определению функции описывают реализацию.

Объявление функции


Объявление каждой функции должно иметь комментарий (прямо перед объявлением), что функция делает и как ей пользоваться. Комментарий можно опустить, только если функция простая и использование очевидно (например, функции вычитывания значений переменных). Старайтесь начинать комментарии в изъявительном наклонении («Открывает файл»). Использование повелительного наклонение («Открыть файл») — не рекомендуется. Комментарий описывает суть функции, а не то, как она это делает.

В комментарии к объявлению функции обратите внимание на следующее:

  • Что подаётся на вход функции, что возвращается в результате.
  • Для функции-члена класса: сохраняет ли экземпляр ссылки на аргументы,
    нужно ли освобождать память.
  • Выделяет ли функция память, которую должен удалить вызывающий код.
  • Могут ли быть аргументы nullptr.
  • Сложность (алгоритмическая) функции.
  • Допустим ли одновременный вызов из разных потоков. Что с синхронизацией?

Пример:

// Возвращает итератор по таблице. Клиент должен удалить 
// итератор после использования. Нельзя использовать итератор
// если соответствующий объект GargantuanTable был удалён.
//
// Итератор изначально указывает на начало таблицы.
//
// Этот метод эквивалентен следующему:
//    Iterator* iter = table->NewIterator();
//    iter->Seek("");
//    return iter;
// Если вы собираетесь сразу же делать новую операцию поиска,
// быстрее будет вызвать NewIterator() и избежать лишней операции поиска.
Iterator* GetIterator() const;

Однако не стоит разжёвывать очевидные вещи.

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

Комментируя конструкторы и деструкторы, учитывайте, что читатель кода знает их назначение. Поэтому комментарийтипа «разрушает этот объект» — бестолковый. Можете описывать, что конструктор делает с аргументами (например, изменение владения на указатели)или какие именно операции по очистке делает деструктор.Если всё и так понятно — ничего не комментируйте. Вообще, обычно деструкторы не имеют комментариев (при объявлении).

Определение функций


Если есть какие-то хитрости в реализации функции, то можнок определению добавить объяснительный комментарий. В нём можноописать трюки с кодом, дать обзор всех этапов вычислений, объяснить выбор той или иной реализации (особенно если естьболее лучшие альтернативы). Можете описать принципы синхронизациикусков кода (здесь блокируем, а здесь рыбу заворачиваем).

Отметим что вы не должны повторять комментарий изобъявления функции (из .h файла или т.п.).Можно кратко описать, что функция делает, однако основной упордолжен быть как она это делает.

Комментарии к переменным


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

Член данных класса


Назначение каждого члена класса должно быть очевидно. Если естьнеочевидные тонкости (специальные значения, завязки с другими членами, ограниченияпо времени жизни) — всё это нужно комментировать. Однако, если типа и именидостаточно — комментарии добавлять не нужно.

С другой стороны, полезными будут описания особых (и неочевидных)значений (nullptr или -1). Например:

private:
 // Используется для проверки выхода за границы
 // -1 - показывает, что мы не знаем сколько записей в таблице
 int num_total_entries_;

Глобальные переменные


Ко всем глобальным переменным следует писать комментарий о ихназначении и (если не очевидно) почему они должны быть глобальными.Например:

// Общее количество тестов, прогоняемых в регрессионом тесте
const int kNumTestCases = 6;

Комментарии к реализации


Комментируйте реализацию функции или алгоритма в случаеналичия неочевидных, интересных, важных кусков кода.

Описательные комментарии


Блоки кода, отличающиеся сложностью или нестандартностью, должны предваряться комментарием. Например:

// Делим результат на 2. Переменная x содержит флаг переноса
for (int i = 0; i < result->size(); ++i) {
  x = (x << 8) + (*result)[i];
  (*result)[i] = x >> 1;
  x &= 1;
}

Построчные комментарии


Строки кода с неочевидным смыслом желательно дополнять комментарием (обычно располагаемым в конце строки).Этот комментраий должен отделяться от кода 2-мя проблами. Например:

// Мапируем блок данных, если объём позволяет
mmap_budget = max<int64>(0, mmap_budget - index_->length());
if (mmap_budget >= data_size_ && !MmapData(mmap_chunk_bytes, mlock))
  return;  // Ошибку уже логировали

Отметим, что здесь 2 комментария на блок кода: один описывает что код делает, другой напоминает, что ошибка уже в логе, если идёт возврат из функции.

Комментарии к аргументам функций


Когда назначение аргумента функции неочевидно, подумайте о следующих вариантах:

  • Если аргумент есть фиксированное значение (literal constant) и это значение
    используется в разных блоках кода (и подразумевается, что это значение суть одно и тоже),
    вам следует создать константу и явно использовать её.
  • Возможно следует заменить аргумент типа bool на
    перечисление (enum). Это сделает аргумент само-определяющим.
  • Для функций, использующих несколько конфигурационных опций в аргументах, можно
    создать отдельный класс (или структуру), объединяющий все опции. И передавать в функцию
    экземпляр этого класса.
    Такой подход имеет несколько преимуществ: опции обозначаются именами, что объясняет
    их назначение. Уменьшается количество аргументов в функции — код легче писать и читать.
    И если вам понадобится добавить ещё опций, менять сам вызов функции не придётся.
  • Вместо сложных выражений в аргументах используйте промежуточную переменную, которой присвойте выражение.
  • В крайнем случае пишите комментарии в месте вызова для прояснения назначения аргументов.

Рассмотрим примеры:

// И какое назначение аргументов?
const DecimalNumber product = CalculateProduct(values, 7, false, nullptr);

Попробуем причесать код:

ProductOptions options;
options.set_precision_decimals(7);
options.set_use_cache(ProductOptions::kDontUseCache);
const DecimalNumber product =
    CalculateProduct(values, options, /*completion_callback=*/nullptr);

Что делать не нужно


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

Сравним:

// Ищём элемент в векторе.  <-- Плохо: очевидно же!
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) {
  Process(element);
}

С этим:

// Обрабатывает (Process) "element" пока есть хоть один
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) {
  Process(element);
}

Само-описывающий код вообще не нуждается в комментариях.
Комментарий на код выше может быть вообще очевидным (и не нужным):

if (!IsAlreadyProcessed(element)) {
  Process(element);
}

Пунктуация, орфография и грамматика


Обращайте внимание на пунктуацию, орфографию и грамматику: намного проще читатьграмотно написанные комментарии.

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

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

Комментарии TODO


Используйте комментарии TODO для временного кода или достаточно хорошего (промежуточного, не идеального) решения.

Комментарий должен включать строкуTODO (все буквы прописные), за ней имя, адрес e-mail, ID дефекта или другаяинформация для идентификации разработчика и сущности проблемы, для которой написан TODO.Цель такого описания — возможность потом найти больше деталей.Наличие TODO с описанием не означает, что указанный программистисправит проблему. Поэтому, когда вы создаёте TODO, обычнотам указано Ваше имя.

// TODO(kl@gmail.com): Используйте "*" для объединения.
// TODO(Zeke) Изменить для связывания.
// TODO(bug 12345): удалить фунционал "Последний посетитель".

Если ваш TODO вида «В будущем сделаем по-другому», тоуказывайте либо конкретную дату («Исправить в ноябре 2005»), либо событие(«Удалить тот код, когда все клиенты будут обрабатывать XML запросы»).

Прим.:
— ссылки могут вести на ещё не переведённые разделы руководства.
— обсуждение вопросов общего характера лучше вести в Часть 1. Вступление
Теги:
Хабы:
+4
Комментарии0

Публикации

Изменить настройки темы

Истории

Работа

Программист C++
118 вакансий
QT разработчик
6 вакансий

Ближайшие события

PG Bootcamp 2024
Дата16 апреля
Время09:30 – 21:00
Место
МинскОнлайн
EvaConf 2024
Дата16 апреля
Время11:00 – 16:00
Место
МоскваОнлайн
Weekend Offer в AliExpress
Дата20 – 21 апреля
Время10:00 – 20:00
Место
Онлайн