Важное замечание
На изменение каждого аспекта стиля лучше давать не меньше недели на привыкание. Мозгу легче переучиваться, если изменения небольшие, легко находить аналогии со старым кодом. А если применить сразу все изменения, то код выйдет настолько непривычным, что мозг перегрузится и возникнет отвращение от слишком резкой смены.
Обоснование
Дело не в «что красивее». Мозг одинаково хорошо привыкнет к описанному ниже стилю, но если код короче, то на один экран помещается больше. Прокручивать код надо реже — а значит реже переносить руку с клавиатуры на мышь, или ездить туда-сюда курсором. Не надо переключать виды деятельности на подпрограмму «прокрутка» (насколько прокручивать? достаточно или надо еще?), даже если она кажется незаметной, и отнимает всего секунду-две на каждый вызов. (Программисты должны ужаснутся — целая секунда на каждый вызов! Как вы будете конкурировать с роботами?). Меньше приходится печатать. Более короткая программа быстрее компилируется (особенно если удастся занять на один дисковый кластер меньше), легче просматривается в системах контроля версий, и т. п.
Вертикальные размеры
Новая функция и отступы
Между разными функциями обычно ставят пустую строку. Но для мозга это потенциально избыточная информация: перед новой функцией идет строка с закрывающей угловой скобкой, ее можно рассматривать как пустую. И эта закрывающая скобка идет без лишних отступов. Новый блок (функция) тоже идет без лишних отступов. В сумме мозгу этого достаточно, чтобы после переобучения прекрасно ориентироваться без лишней пустой строки.
|
|
|
|
Можно подумать о коде на Питоне — там как раз нету закрывающих скобок для блоков кода, и поэтому там ставят только одну пустую строку между функциями. С этим подходом, визуально ваш код на С-подобном языке будет похож на код на Питоне — если принимать, что закрывающая скобка } на новой строке равна пустой строке на Питоне (а она визуально почти равна — одна еле заметная палочка). Тогда количество строк кода для аналогичной программы на Питоне сравняется. Бойтесь питонщиков, которые выигрывают за счет краткости своего кода!
Автозамена регулярными выражениями: \}\n\n -> \}\n.
Вначале было непривычно. Но за неделю подсознание привыкло, и пошел дальше. А программируя на Питоне, теперь я бы убрал пустую строку после блоков совсем — ведь достаточно отступов!
Комментарии
/*многострочный комментарий неправильно */ /* многострочный комментарий правильно */
Код читается намного чаще, чем пишется. Поэтому удобство написания (не следить за */) менее важно, чем удобство чтения. Чтобы понять, что комментарий кончился, мозгу достаточно расцветки кода, а */ на новой пустой строке совсем не нужны. Если нужны, то достаточно переучится, и потребность пропадет. Некоторые люди могут возразить, что не все смогут отличить комментарии по цвету. Но дальтоники могут использовать //.
Это небольшое изменение стиля, поэтому на него можно не тратить неделю.
Include guard
Все современные компиляторы С++ поддерживают pragma once, поэтому include guard на основе ifndef/define/endif надо удалить и заменить. Пропадает опасность коллизии имен символов препроцессора, код становится короче на 2-4 строки (учитывая пустые строки), и быстрее компилируется (препроцессору не надо просматривать файл до конца в случае срабатывания guard). Далее, pragma once, если он есть, должен идти без лишних пустых строк в начале и конце. Подсознание привыкнет, что первая строка кода в файле (после комментариев начала файла) всегда pragma once, и отделит его от комментария по цвету. А даже если вы дальтоник, то подсознание отделит этот блок по набору // у комментариев и # у pragma once.
Это небольшое изменение, поэтому на него можно не тратить целую неделю. Автозамена:
\#pragma once\n\n -> \#pragma once\n \n\n\#pragma once -> \n\#pragma once
Пустая строка в начале функции
Начало функции или другого блока итак хорошо видно — хотя бы по уровню отступа. И строки внутри функции имеют дополнительный отступ. Поэтому (после переобучения) пустая строка после начала функции не нужна.
|
|
|
Напротив, пустая строка сбивает с толку: зачем ее оставили? Почему такое отступление от общих правил? Может и после if ставить пустые строки? Кому-то не достаточно увеличения отступа? Зачем было обучаться такому исключению из правил? Кому это выгодно?
Автозамена: {\n\n -> {\n
Через неделю использования нового подхода он становится привычным, и можно двигаться дальше.
#include
#include (если они есть) в начале файла должны идти сразу же, без лишних пустых строк (сразу после комментария начала файла или pragma once). После блока с include обычно ставлю forward declarations, тоже без пустой строки — ведь у них общее назначение, позволить программе собраться без ошибок (с точки зрения java-программиста, который не должен подключать лишние include и forward declarations). Forward declarations легко отличаются от #include по цвету и отсутствию #. Но после блока include/forward declarations я ставлю пустую строку — чтобы отделять например создание нового класса от forward declarations, потому что у них одинаковые отступы, и надо уметь быстро замечать, где начало основного содержимого файла.
Комментарии начала файла
В первом комментарии обычно ставится имя файла, имя создателя и лицензия. По возможности, имя файла и имя создателя переводится на первую строку, и лицензия ужимается до одной строки вместо копирования целого абзаца.
//file.cpp by AuthorName //This file is licensed under GPL, www.gnu.org/copyleft/gpl.html
Горизонтальные размеры.
Унификация отступов внутри класса
В классах, метки области видимости типа public/private должны идти под новыми отступами, и код под этими метками — под еще одним отступом.
|
|
|
Зачем новый отступ? Да все затем же — чтобы быстро находить, где начало блока («а это какая область видимости?») и не сбиваться с толку — не путать начало класса и «public:» как область видимости, если у них одинаковые отступы («а где начинается этот класс? Не пропустить бы во время прокрутки...»). Почему принято иначе? Подозреваю, что дело идет от ширины экрана и меток для goto: «public:» похоже на создание новой метки для goto, и древние текстовые редакторы могли автоматически его выравнивать, как и метку. И древние программисты на ассемблере привыкли, что метки идут с самого слева, а не по уровням вложенности кода. Низкое разрешение древних ЭЛТ-мониторов способствовало закреплению такого подхода (на лишнюю табуляцию мало места). Но если разрешение вашего экрана больше, чем 320*240, то можно смело отказываться от этого атавизма, и с ухмылкой исправлять предложения текстового редактора превратить иерархические блоки внутри класса в аналог одноуровневого goto.
Эта унификация упрощает жизнь: ведь все блоки идут с новым отступом, почему для меток области видимости должно быть иначе? Мозг привыкает и с облегчением замечает, что больше не надо заботится об исключениях из этого правила. Код становится еще более питоноподобным. Через неделю можно двигаться дальше.
Пустые функции
Пустые виртуальные деструкторы чаще всего так и остаются пустыми. Их пустое тело лучше прописывать прямо в определении класса, чтобы читатель кода не думал — «а вдруг там что-то есть? Надо посмотреть».
Длинные имена классов
Если лень писать такое:
VeryLongClassName* veryLongClassName = new VeryLongClassName(params);
то можно писать так:
auto veryLongClassName = new VeryLongClassName(params);
и даже так:
New<VeryLongClassName> shortName(params);
что возможно, если применять шаблонный класс New (умный указатель).
template<class Class> class New { private: Class*d; public: New() { d = new Class(); } template<typename T0> New(T0 t0) { d = new Class(t0); } template<typename T0, typename T1> New(T0 t0, T1 t1) { d = new Class(t0, t1); } template<typename T0, typename T1, typename T2> New(T0 t0, T1 t1, T2 t2) { d = new Class(t0, t1, t2); } template<typename T0, typename T1, typename T2, typename T3> New(T0 t0, T1 t1, T2 t2, T3 t3) { d = new Class(t0, t1, t2, t3); } virtual ~New() {} Class* operator->() { return d; } const Class* operator->()const { return d; } operator Class* () { return d; } operator const Class* ()const { return d; } };
В последнем случае, если конструктор без параметров, такие переменные можно ставить в определение класса, и тогда не надо будет следить за созданием объекта. И это все — не дожидаясь нововведений С++11 в VS2012.
Имена
Как принято в Java/Qt: ClassName, functionName. Плохо: function_name — ведь дольше набирать, тянуть палец к далеко расположенной клавише подчеркивания, дольше читать, мозгу сложнее окинуть имя сразу одним взглядом (вместо прочтения длинного слова по частям с переводом взгляда чуть правее), как рекомендуется в техниках обучения быстрому чтению. «Программный апгрейд» (привычка к другому стилю кода) важен, потому что физически проапгрейдить глаза затруднительно (чтобы они быстрее двигались в горизонтальной плоскости).
Tabs
Табуляция, не пробелы! habrahabr.ru/post/118208
Запятые перед именами
В списках инициализации или списках переменных функций, такие конструкции:
|
|
надо отменить, и перенести запятые в конец строки. Зачем вообще запятые ставить в начало? Раньше я их ставил, чтобы 1) не следить за последней запятой — если добавляется новая строка, то не надо добавлять запятую в конец прошлой, достаточно написать в начало текущей, аналогично и при удалении, переносе строк между классами 2) чтобы визуально подчеркнуть блок однотипного кода, да и приятно смотреть на выровненные символы. По поводу 1 — код читается намного чаще, чем пишется, поэтому один символ — слабый аргумент. Зато такой код хуже воспринимается: чтение происходит с левой стороны, программист итак знает, что это список инициализации или список параметров функции, и ему важнее видеть имена переменных, а не сначала пропускать запятые и уж затем начинать читать переменные. Таким образом, второстепенная информация и ненужные украшательства ставятся на первое место, а более важная информация — на второе. Такой подход словно говорит нам: «Обратите внимание, переменные разделяются запятыми! Это не инструкции с точками с запятой, это запятые! Это очень важно! Ведь вы могли забыть, что списки инициализации разделяются запятыми — а вдруг вы новичок, и не знаете? Без запятой всему конец — а ее так легко пропустить! И компилятор не напомнит, что запятая пропущена — и что же тогда делать? А вдруг вам захочется скопировать запятую, а она уже тут как тут — в самом начале строки! А вы замечали, что дело можно развернуть и так, что запятые могут быть важнее переменных? Это ведь смотря на что обращать внимание, ведь все относительно! Как приятно видеть знакомую запятую в начале строки, вместо незнакомых имен переменных!». Долой украшательства, да здравствует удобство чтения более важной информации.
Заключение
Плавное изменение стиля кода для С-подобных языков может повысить конкурентоспособность этих языков и программистов их использующих, чуть приблизить их к другим языкам по краткости (например к Питону). Для С++ это особенно важно — учитывая, насколько программы на других языках бывают короче (взять тот же linq для C#). Экономьте электроэнергию и ресурс клавиатуры, всего хорошего!

