90 рекомендаций по стилю написания программ на C++

http://geosoft.no/development/cppstyle.html
  • Перевод
От переводчика. Искал в интернете простой и легко применимый гайдлайн по написанию программ на C++. Мне понравился один из вариантов, и я решил его перевести и опубликовать. Если хабрапользователи хорошо встретят этот топик, могу перевести и другие связанные документы, а также гайдлайны по написанию кода от других компаний.

1 Введение


Настоящий документ содержит рекомендации по написанию программ на языке C++.

Рекомендации основаны на установившихся стандартах, собранных из различных источников, личного опыта, частных требований и потребностей определённых проектов, а также почерпнутых из источников (см. ниже).

Но для появления ещё одного списка рекомендаций, помимо указанных источников, есть несколько причин. Основная причина — их излишняя обобщённость, поскольку зачастую требуется задать частные правила (в особенности правила именования). Данный документ содержит комментарии, что делает его более удобным в использовании при проведении ревизий кода, чем другие уже существующие документы. К тому же, рекомендации по программированию обычно вперемешку содержат описания проблем стиля и технических проблем, что не совсем удобно. Этот документ не содержит каких-либо технических рекомендаций по C++, делая упор на вопросах стиля.

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

1.1 Формат документа

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

Рекомендации отображаются следующим образом:

n. Короткое описание рекомендации.
// Пример кода (если возможно)
Объяснение, происхождение и дополнительная информация.

Комментарии к рекомендациям особенно важны, поскольку стандарты написания кода и гайдлайны обычно разжигают «холивары», и важным моментом является объяснение рекомендации.

1.2 Важность рекомендаций

Рекомендации разделены по степени важности: обязательные, настоятельно рекомендуемые и общие.

2 Общие рекомендации


1. Допускаются любые нарушения рекомендаций, если это улучшает читаемость.

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

2. Правила могут быть нарушены, если против них есть персональные возражения.

Это попытка создать набор общих рекомендаций, не навязывая всем единый стиль. Опытные программисты обычно всё равно подгоняют стиль под себя. Подобный список рекомендаций, имеющийся под рукой (или хотя бы требование ознакомиться с ним), обычно заставляет людей задумываться о стиле программирования и оценке их собственных практик в этой области.

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

3 Соглашения об именовании


3.1 Общие соглашения об именовании

3. Имена, представляющие типы, должны быть обязательно написаны в смешанном регистре, начиная с верхнего.

Line, SavingsAccount

Общая практика в сообществе разработчиков C++.

4. Имена переменных должны быть записаны в смешанном регистре, начиная с нижнего.

line, savingsAccount

Общая практика в сообществе разработчиков C++. Позволяет легко отличать переменные от типов, предотвращает потенциальные коллизии имён, например: Line line;

5. Именованные константы (включая значения перечислений) должны быть записаны в верхнем регистре с нижним подчёркиванием в качестве разделителя.

MAX_ITERATIONS, COLOR_RED, PI

Общая практика в сообществе разработчиков C++. Использование таких констант должно быть сведено к минимуму. В большинстве случаев реализация значения в виде метода — лучшее решение:

  int getMaxIterations() // НЕЛЬЗЯ: MAX_ITERATIONS = 25
  {
    return 25;
  }

Эта форма более читаемая и гарантирует единый интерфейс к значениям, хранящимся в классе.

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

getName(), computeTotalWidth()

Совпадает с правилом для переменных, но отличие между ними состоит в их специфических формах.

7. Названия пространств имён следует записывать в нижнем регистре.

model::analyzer, io::iomanager, common::math::geometry

Общая практика в сообществе разработчиков C++.

8. Следует называть имена типов в шаблонах одной заглавной буквой.

template<class T> ...
template<class C, class D> ...

Общая практика в сообществе разработчиков C++. Позволяет выделить имена шаблонов среди других используемых имён.

9. Аббревиатуры и сокращения в именах должны записываться в нижнем регистре.
exportHtmlSource(); // НЕЛЬЗЯ: exportHTMLSource();
openDvdPlayer();    // НЕЛЬЗЯ: openDVDPlayer();

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

10. Глобальные переменные всегда следует использовать с оператором разрешения области видимости (::).

::mainWindow.open(), ::applicationContext.getName()

Следует избегать использования глобальных переменных. Предпочтительнее использование синглтонов.

11. Членам класса с модификатором private следует присваивать суффикс-подчёркивание.

class SomeClass {
  private:
    int length_;
}

Не считая имени и типа, область видимости — наиболее важное свойство переменной. Явное указание модификатора доступа в виде подчёркивания избавляет от путаницы между членами класса и локальными переменными. Это важно, поскольку переменные класса имеют большее значение, нежели переменные в методах, и к ним следует относиться более осторожно.

Дополнительным эффектом от суффикса-подчёркивания является разрешение проблемы именования в методах, устанавливающих значения, а также в конструкторах:

  void setDepth (int depth)
  {
    depth_ = depth;
  }

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

12. Настраиваемым переменным следует давать то же имя, что и у их типа.

void setTopic(Topic* topic)      // НЕЛЬЗЯ: void setTopic(Topic* value)
                                 // НЕЛЬЗЯ: void setTopic(Topic* aTopic)
                                 // НЕЛЬЗЯ: void setTopic(Topic* t)

void connect(Database* database) // НЕЛЬЗЯ: void connect(Database* db)
                                 // НЕЛЬЗЯ: void connect (Database* oracleDB)

Сокращайте сложность путём уменьшения числа используемых терминов и имён. Также упрощает распознавание типа просто по имени переменной.

Если по какой-то причине эта рекомендация кажется неподходщей, это означает, что имя типа выбрано неверно.

Не являющиеся настраиваемыми переменные могут быть названы по их назначению и типу:

  Point  startingPoint, centerPoint;
  Name   loginName;


13. Все имена следует записывать по-английски.

fileName;   // НЕ РЕКОМЕНДУЕТСЯ: imyaFayla

Английский наиболее предпочитетелен для интернациональной разработки.

14. Переменные, имеющие большую область видимости, следует называть длинными именами, имеющие небольшую область видимости — короткими.

Имена временных переменных, использующихся для хранения временных значений или индексов, лучше всего делать короткими. Программист, читающий такие переменные, должен иметь возможность предположить, что их значения не используются за пределами нескольких строк кода. Обычно это переменные i, j, k, l, m, n (для целых), а также c и d (для символов).

15. Имена объектов не указываются явно, следует избегать указания названий объектов в именах методов.

line.getLength();   // НЕ РЕКОМЕНДУЕТСЯ: line.getLineLength();

Второй вариант смотрится вполне естественно в объявлении класса, но совершенно избыточен при использовании, как это и показано в примере.

(Пункт № 16 отсутствует.— Примечание переводчика.)

3.2 Особые правила именования

17. Слова get/set должны быть использованы везде, где осуществляется прямой доступ к атрибуту.

employee.getName();
employee.setName(name);

matrix.getElement(2, 4);
matrix.setElement(2, 4, value);

Общая практика в сообществе разработчиков C++. В Java это соглашение стало более-менее стандартным.

18. Слово compute может быть использовано в методах, вычисляющих что-либо.

valueSet->computeAverage();
matrix->computeInverse()

Дайте читающему сразу понять, что это времязатратная операция.

19. Слово find может быть использовано в методах, осуществляющих какой-либо поиск.

vertex.findNearestVertex();
matrix.findMinElement();

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

20. Слово initialize может быть использовано там, где объект или сущность инициализируется.

printer.initializeFontSet();

Следует отдавать предпочтение американскому варианту initialize, нежели британскому initialise. Следует избегать сокращения init.

21. Переменным, представляющим GUI, следует давать суффикс, соответствующий имени типа компонента.

mainWindow, propertiesDialog, widthScale, loginText,
leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle и т. д.

Улучшает читаемость, поскольку имя даёт пользователю прямую подсказку о типе переменной и, следовательно, ресурсах объектов.

22. Множественное число следует использовать для представления наборов (коллекций) объектов.

vector<Point>  points;
int            values[];

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

23. Префикс n следует использовать для представления числа объектов.

nPoints, nLines

Обозначение взято из математики, где оно является установившимся соглашением для обозначения числа объектов.

24. Суффикс No следует использовать для обозначения номера сущности.

tableNo, employeeNo

Обозначение взято из математики, где оно является установившимся соглашением для обозначения номера сущности.

Другой неплохой альтернативой является префикс i: iTable, iEmployee. Он ясно даёт понять, что перед нами именованный итератор.

25. Переменным-итераторам следует давать имена i, j, k и т. д.

for (int i = 0; i < nTables); i++) {
  :
}

for (vector<MyClass>::iterator i = list.begin(); i != list.end(); i++) {
  Element element = *i;
  ...
}

Обозначение взято из математики, где оно является установившимся соглашением для обозначения итераторов.

Переменные с именами j, k и т. д. рекомендуется использовать только во вложенных циклах.

26. Префикс is следует использовать только для булевых (логических) переменных и методов.

isSet, isVisible, isFinished, isFound, isOpen

Общая практика в сообществе разработчиков C++, иногда используемая и в Java.

Использование этого префикса избавляет от таких имён, как status или flag. isStatus или isFlag просто не подходят, и программист вынужден выбирать более осмысленные имена.

В некоторых ситуациях префикс is лучше заменить на другой: has, can или should:

  bool hasLicense();
  bool canEvaluate();
  bool shouldSort();


27. Симметричные имена должны использоваться для соответствующих операций.

get/set, add/remove, create/destroy, start/stop, insert/delete,
increment/decrement, old/new, begin/end, first/last, up/down, min/max,
next/previous, old/new, open/close, show/hide, suspend/resume, и т. д.

Уменьшайте сложность за счёт симметрии.

28. Следует избегать сокращений в именах.

computeAverage();   // НЕЛЬЗЯ: compAvg();

Рассмотрим два вида слов. Первые — обычные слова, перечисляемые в словарях, которые нельзя сокращать. Никогда не сокращайте:

cmd    вместо    command
cp     вместо    copy
pt     вместо    point
comp   вместо    compute
init   вместо    initialize
и т. д.


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

 HypertextMarkupLanguage  вместо    html
 CentralProcessingUnit    вместо    cpu
 PriceEarningRatio        вместо    pe
 и т. д.


29. Следует избегать дополнительного именования указателей.

Line* line; // НЕ РЕКОМЕНДУЕТСЯ: Line* pLine;
            // НЕ РЕКОМЕНДУЕТСЯ: Line* linePtr;

Множество переменных в C/C++ являются указателями. Только в том случае, когда тип объекта в языке C++ особенно важен, имя должно отражать его.

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

bool isError; // НЕЛЬЗЯ: isNoError
bool isFound; // НЕЛЬЗЯ: isNotFound

Проблема возникает, когда такое имя используется в конъюнкции с оператором логического отрицания, что влечёт двойное отрицание. Результат не обязательно будет отрицанием !isNotFound.

31. Константы в перечислениях могут иметь префикс — общее имя типа.

enum Color {
  COLOR_RED,
  COLOR_GREEN,
  COLOR_BLUE
};

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

Другим подходом является обращение к константам по их общему типу: Color::RED, Airline::AIR_FRANCE и т. д.

Обратите внимание, что имя перечисления обычно записано в единственном числе, например: enum Color {...}. Имя во множественном числе хорошо выглядит при объявлении, но не очень хорошо подходит для практического использования.

32. Классам исключений следует присваивать суффикс Exception.

class AccessException
{
  :
}

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

33. Функциям (методам, возвращающим какие-либо значения) следует давать имена в зависимости от того, что они возвращают, а процедурам — в зависимости от того, что они выполняют (методы void).

Улучшайте читаемость. Такое именование даёт понять, что метод делает, а что нет, а также избавляет код от потенциальных побочных эффектов.

4 Файлы


4.1 Файлы исходных кодов

34. Заголовочным файлам C++ следует давать расширение .h (предпочтительно) либо .hpp. Файлы исходных кодов могут иметь расширения .c++ (рекомендуется), .C, .cc либо .cpp.

MyClass.c++, MyClass.h

Это расширения, одобряемые стандартом C++.

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

MyClass.h, MyClass.c++


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

36. Все определения должны находиться в файлах исходного кода.

class MyClass
{
public:
  int getValue () {return value_;}  // НЕЛЬЗЯ!
  ...

private:
  int value_;
}

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

37. Содержимое файлов не должно превышать 80 колонок.

80 колонок — широко распространённое разрешение для редакторов, эмуляторов терминалов, принтеров и отладчиков; файлы передаются между различными людьми, поэтому нужно придерживаться этих ограничений. Уместная разбивка строк улучшает читаемость при совместной работе над исходным кодом.

38. Нельзя использовать специальные символы (например, TAB) и разрывы страниц.

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

39. Незавершённость разбитых строк должна быть очевидна.

totalSum = a + b + c +
           d + e;

function (param1, param2,
          param3);

setText ("Long line split"
         "into two parts.");

for (int tableNo = 0; tableNo < nTables;
     tableNo += tableStep) {
  ...
}

Разбивка строк появляется, когда ограничение на 80 колонок, описанное выше, нарушается. Сложно дать жёсткие правила по разбивке, но примеры выше показывают общие принципы.

В общем случае:
  • разрыв после запятой;
  • разрыв после оператора;
  • выравнивание новой строки с началом выражения на предыдущей строке.


4.2 Включения файлов

40. Заголовочные файлы должны содержать защиту от вложенного включения.

#ifndef COM_COMPANY_MODULE_CLASSNAME_H
#define COM_COMPANY_MODULE_CLASSNAME_H
  :
#endif // COM_COMPANY_MODULE_CLASSNAME_H

Конструкция позволяет избегать ошибок компиляции. Это соглашение позволяет увидеть положение файла в структуре проекта и предотвращает конфликты имён.

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

#include <fstream>
#include <iomanip>

#include <qt/qbutton.h>
#include <qt/qtextfield.h>

#include "com/company/ui/PropertiesDialog.h"
#include "com/company/ui/MainWindow.h"

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

42. Директивы включения должны располагаться только в начале файла.

Общая практика. Избегайте нежелательных побочных эффектов, которые может вызвать «скрытое» включение где-то в середине файла исходного кода.

5 Выражения


5.1 Типы

43. Локальные типы, используемые в одном файле, должны быть объявлены только в нём.

Улучшает сокрытие информации.

44. Разделы класса public, protected и private должны быть отсортированы. Все разделы должны быть явно указаны.

Сперва должен идти раздел public, что избавит желающих ознакомиться с классом от чтения разделов protected/private.

45. Приведение типов должно быть явным. Никогда не полагайтесь на неявное приведение типов.

floatValue = static_cast<float>(intValue); // НЕЛЬЗЯ: floatValue = intValue;

Этим программист показывает, что ему известно о различии типов, что смешение сделано намеренно.

5.2 Переменные

46. Следует инициализировать переменные в месте их объявления.

Это даёт гарантию, что переменные пригодны для использования в любой момент времени. Но иногда нет возможности осуществить это:

  int x, y, z;
  getCenter(&x, &y, &z);

В этих случаях лучше оставить переменные неинициализированными, чем присваивать им какие-либо значения.

47. Переменные никогда не должны иметь двойной смысл.

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

48. Следует избегать использования глобальных переменных.

Не существует причины использовать глобальные переменные в C++ (на самом деле существует.— Примечание переводчика). То же касается глобальных функций и (статических) переменных, область видимости которых — весь файл.

49. Не следует объявлять переменные класса как public.

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

Обратите внимание, что структуры в языке C++ оставлены только для совместимости с C; их использование ухудшает читаемость кода. Вместо структур используйте классы.

(Пункт № 50 отсутствует.— Примечание переводчика.)

51. Символ указателя или ссылки в языке C++ следует ставить сразу после имени типа, а не с именем переменной.

float* x; // НЕ РЕКОМЕНДУЕТСЯ: float *x; 
int& y;   // НЕ РЕКОМЕНДУЕТСЯ: int &y;

То, что переменная — указатель или ссылка, относится скорее к её типу, а не к имени. Программисты на C часто используют другой подход, но в C++ лучше придерживаться этой рекомендации.

(Пункт № 52 отсутствует.— Примечание переводчика.)

53. Следует избегать неявного сравнения булевых (логических) переменных и указателей с нулём.

if (nLines != 0)  // НЕ РЕКОМЕНДУЕТСЯ: if (nLines)
if (value != 0.0) // НЕ РЕКОМЕНДУЕТСЯ: if (value)

Стандарт C++ не гарантирует, что значения переменных int и float, равные нулю, будут представлены как бинарный 0. Также при явном сравнении видно сравниваемый тип.

Логично было бы предположить, что также и указатели не следует неявно сравнивать с нулём, например, if (line == 0) вместо if (line). Последнее является очень распространённой практикой в C/C++, поэтому также может быть использовано.

54. Переменные следует объявлять в как можно меньшей области видимости.

Это упрощает контроль над действием переменной и сторонними эффектами.

5.3 Циклы

55. Нельзя включать в конструкцию for() выражения, не относящиеся к управлению циклом.

sum = 0;                       // НЕЛЬЗЯ: for (i = 0, sum = 0; i < 100; i++)
for (i = 0; i < 100; i++)                sum += value[i];
  sum += value[i];

Улучшайте поддержку и читаемость. Строго разделяйте контроль над циклом и то, что в нём содержится.

56. Переменные, относящиеся к циклу, следует инициализировать непосредственно перед ним.

isDone = false;           // НЕ РЕКОМЕНДУЕТСЯ: bool isDone = false;
while (!isDone) {         //      :
  :                       //      while (!isDone) {
}                         //        :
                          //      }


57. Можно избегать циклов do-while.

Такие циклы хуже читаемы, поскольку условие описано после тела. Читающему придётся просмотреть весь цикл, чтобы понять его работу.

Циклы do-while вообще не являются острой необходимостью. Любой такой цикл может быть заменён на цикл while или for.

Меньшее число используемых конструкций улучшает читаемость.

58. Следует избегать использования break и continue в циклах.

Такие выражения следует использовать только тогда, когда они повышают читаемость.

(Пункт № 59 отсутствует.— Примечание переводчика.)

60. Для бесконечных циклов следует использовать форму while (true) .

while (true) {
  :
}

for (;;) {  // НЕТ!
  :
}

while (1) { // НЕТ!
  :
}

Проверка на единицу не является необходимой и бессмысленна. Форма for (;;) не очень читаема; также не является очевидным, что цикл бесконечный.

5.4 Условные выражения

61. Строго избегайте сложных уловных выражений. Вместо этого вводите булевы переменные.

bool isFinished = (elementNo < 0) || (elementNo > maxElement);
bool isRepeatedEntry = elementNo == lastElement;
if (isFinished || isRepeatedEntry) {
  :
}

// NOT:
if ((elementNo < 0) || (elementNo > maxElement)||
     elementNo == lastElement) {
  :
}

Задание булевых переменных для выражений приведёт к самодокументированию программы. Конструкцию будет легче читать, отлаживать и поддерживать.

62. Ожидаемую часть следует располагать в части if, исключение — в части else.

bool isOk = readFile (fileName);
if (isOk) {
  :
}
else {
  :
}

Это позволяет убедиться, что исключения не вносят неясности в нормальный ход выполнения. Важно для читаемости и производительности.

63. Условие следует размещать в отдельной строке.

if (isDone)       // НЕ РЕКОМЕНДУЕТСЯ: if (isDone) doCleanup();
  doCleanup();

Применяется для отладки.

64. Следует строго избегать исполнимых выражений в условиях.

File* fileHandle = open(fileName, "w");
if (!fileHandle) {
  :
}

// НЕЛЬЗЯ:
if (!(fileHandle = open(fileName, "w"))) {
  :
}

Исполняемые выражения в условиях усложняют читаемость. Особенно это касается новичков в С/С++.

5.5 Разное

65. Следует избегать «магических» чисел в коде. Числа, отличные от 0 или 1, следует объявлять как именованные константы.

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

66. Константы с плавающей точкой следует записывать с десятичной точкой и с указанием по крайней мере одной цифры после запятой.

double total = 0.0;    // НЕ РЕКОМЕНДУЕТСЯ:  double total = 0;
double speed = 3.0e8;  // НЕ РЕКОМЕНДУЕТСЯ:  double speed = 3e8;

double sum;
:
sum = (a + b) * 10.0;

Это подчёркивает различные подходы при работе с целыми числами и числами с плавающей точкой. С точки зрения математики, эти две модели совершенно различны и не совместимы.

А также (как это показано в последнем примере выше) делается акцент на типе переменной (sum) в том месте, где это не является очевидным.

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

double total = 0.5;  // НЕ РЕКОМЕНДУЕТСЯ:  double total = .5;


Система чисел и выражений в C++ заимствована из математики, и следует придерживаться традиционных форм записи, где это возможно. Помимо прочего, 0.5 — более читаемо, чем .5 (первый вариант никак не спутать с числом 5).

68. У функций нужно обязательно указывать тип возвращаемого значения.

int getValue()   // НЕЛЬЗЯ: getValue()
{
  :
}

Если это не указано явно, C++ считает, что возвращаемое значение имеет тип int. Никогда нельзя полагаться на это, поскольку такой способ может смутить программистов, не знакомых с ним.

69. Не следует использовать goto.

Этот оператор нарушает принципы структурного программирования. Следует использовать только в очень редких случаях (например, для выхода из глубоко вложенного цикла), когда иные варианты однозначно ухудшат читаемость.

70. Следует использовать «0» вместо «NULL».

NULL является частью стандартной библиотеки C и устарело в C++.

6 Оформление и комментарии


6.1 Оформление

71. Основной отступ следует делать в два пробела.

for (i = 0; i < nElements; i++)
  a[i] = 0;

Отступ в один пробел достаточно мал, чтобы отражать логическую структуру кода. Отступ более 4 пробелов делает глубоко вложенный код нечитаемым и увеличивает вероятность того, что строки придётся разбивать. Широко распространены варианты в 2, 3 или 4 пробела; причём 2 и 4 — более широко.

72. Блоки кода следует оформлять так, как показано в примере 1 (рекомендуется) или в примере 2, но ни в коем случае не так, как показано в примере 3. Оформление функций и классов должно следовать примеру 2.

while (!done) {
  doSomething();
  done = moreToDo();
}


while (!done)
{
  doSomething();
  done = moreToDo();
}


while (!done)
  {
    doSomething();
    done = moreToDo();
  }

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

73. Объявления классов следует оформлять следующим образом:

class SomeClass : public BaseClass
{
  public:
    ...

  protected:
    ...

  private:
    ...
}

Частное следствие из правила, указанного выше.

74. Определения методов следует оформлять следующим образом:

void someMethod()
{
  ...
}

Следствие из правила, указанного выше.

75. Конструкцию if-else следует оформлять следующим образом:

if (condition) {
  statements;
}

if (condition) {
  statements;
}
else {
   statements;
}

if (condition) {
  statements;
}
else if (condition) {
  statements;
}
else {
  statements;
}

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

  if (condition) {
    statements;
  } else {
    statements;
  }

Лучше каждую часть if-else помещать на отдельной строке. Это упрощает действия с кодом, например, перемещение блока else.

76. Цикл for следует оформлять следующим образом:

for (initialization; condition; update) {
  statements;
}

Следствие из правила, указанного выше.

77. Цикл for с пустым телом следует оформлять следующим образом:

for (initialization; condition; update)
  ;

Делает акцент для читающего на том, что тело пусто. Однако циклов, не имеющих тела, следует избегать.

78. Цикл while следует оформлять следующим образом:

while (condition) {
  statements;
}

Следствие из правила, указанного выше.

79. Цикл do-while следует оформлять следующим образом:

do {
  statements;
} while (condition);

Следствие из правила, указанного выше.

80. Конструкцию switch следует оформлять следующим образом:

switch (condition) {
  case ABC :
    statements;
    // Отсутствует "break"

  case DEF :
    statements;
    break;

  case XYZ :
    statements;
    break;

  default :
    statements;
    break;
}

Обратите внимание, что каждое слово case имеет отступ относительно всей конструкции, что помогает её выделить. Также обратите внимание на пробелы перед двоеточиями. Если где-то отсутствует ключевое слово break, то предупреждением об этом должен служить комментарий. Программисты часто забывают ставить это слово, поэтому случай нарочного его пропуска должен описываться специально.

81. Конструкцию try-catch следует оформлять следующим образом:

try {
  statements;
}
catch (Exception& exception) {
  statements;
}

Следствие из правила, указанного выше. Вопросы, касающиеся закрывающих фигурных скобок у конструкции if-else, применимы и здесь.

82. Если конструкция if-else содержит только одно выражение в теле, фигурные скобки можно опускать.

if (condition)
  statement;

while (condition)
  statement;

for (initialization; condition; update)
  statement;

Рекомендуется всё же не опускать фигурные скобки.

83. Возвращаемый функцией тип может располагаться над именем самой функции.

void
MyClass::myMethod(void)
{
  :
}

Так функции выровнены в одну колонку.

6.2 Пробелы

84.

— Операторы следует отбивать пробелами.
— После зарезервированных ключевых слов языка C++ следует ставить пробел.
— После запятых следует ставить пробелы.
— Двоеточия следует отбивать пробелами.
— После точек с запятой в цикле for следует ставить пробелы.

a = (b + c) * d; // НЕ РЕКОМЕНДУЕТСЯ: a=(b+c)*d

while (true)   // НЕ РЕКОМЕНДУЕТСЯ: while(true) 
{
  ...

doSomething(a, b, c, d);  // НЕ РЕКОМЕНДУЕТСЯ: doSomething(a,b,c,d);

case 100 :  // НЕ РЕКОМЕНДУЕТСЯ: case 100:

for (i = 0; i < 10; i++) {  // НЕ РЕКОМЕНДУЕТСЯ: for(i=0;i<10;i++){
  ...

Выделяет отдельные части выражений. Улучшает читаемость. Сложно дать всеобъемлющий набор рекомендаций относительно пробелов в языке C++. Рекомендации выше должны показать общие принципы.

85. После имён методов может идти пробел, если далее следует другое имя.

doSomething (currentFile);

Выделяет отдельные имена. Улучшает читаемость. Если далее нет никакого имени, пробел можно опускать (doSomething()).

Другим подходом является указание пробела сразу после открывающей скобки. Использующие его также обычно ставят пробел и перед закрывающей скобкой: doSomething( currentFile );. Это позволяет выделять отдельные имена; пробел перед закрывающей скобкой выглядит неестественно, но без него выражение выглядит несимметрично (doSomething( currentFile);).

86. Логические блоки в коде следует отделять пустой строкой.

Matrix4x4 matrix = new Matrix4x4();

double cosAngle = Math.cos(angle);
double sinAngle = Math.sin(angle);

matrix.setElement(1, 1,  cosAngle);
matrix.setElement(1, 2,  sinAngle);
matrix.setElement(2, 1, -sinAngle);
matrix.setElement(2, 2,  cosAngle);

multiply(matrix);

Улучшает читаемость.

87. Методы рекомендуется отделять тремя пустыми строками.

Это позволяет лучше их выделять.

88. Переменные в объявлениях можно выравнивать.

AsciiFile* file;
int        nPoints;
float      x, y;

Улучшает читаемость. Чётче видны пары тип — переменная.

89. Используйте выравнивание везде, где это улучшает читаемость.

if      (a == lowValue)    compueSomething();
else if (a == mediumValue) computeSomethingElse();
else if (a == highValue)   computeSomethingElseYet();

value = (potential        * oilDensity)   / constant1 +
        (depth            * waterDensity) / constant2 +
        (zCoordinateValue * gasDensity)   / constant3;

minPosition     = computeDistance(min,     x, y, z);
averagePosition = computeDistance(average, x, y, z);

switch (value) {
  case PHASE_OIL   : strcpy(phase, "Oil");   break;
  case PHASE_WATER : strcpy(phase, "Water"); break;
  case PHASE_GAS   : strcpy(phase, "Gas");   break;
}

Есть множество случаев, когда код можно дополнительно выравнивать, даже если это нарушает установленные ранее правила.

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

90. Сложный код, написанный с использованием хитрых ходов, следует не комментировать, а переписывать!

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

91. Все комментарии следует писать на английском.

В интернациональной среде английский — предпочтительный язык.

92. Используйте // для всех комментариев, включая многострочные.
// Комментарий, расположенный
// на нескольких строках.

Если следовать этой рекомендации, многострочные комментарии /* */ можно использовать для отладки и иных целей.

После // следует ставить пробел, а сам комментарий следует начинать писать с большой буквы завершать точкой.

93. Комментарии следует располагать так, чтобы они относились к тому, что они описывают.

while (true) {       // НЕ РЕКОМЕНДУЕТСЯ:  while (true) { 
  // Do something                          // Do something
  something();                               something();
}                                          }

Это делается с тем, чтобы избежать ситуацию, когда комментарии нарушают логическую структуру программы.

94. Комментарии к классам и заголовкам методов следует делать в соответствии с соглашениями JavaDoc.

Программисты на языке Java используют более развитый подход к документированию благодаря стандартному автоматическому средству Javadoc, которое является частью пакета разработки и позволяет автоматически создавать документацию в формате HTML из комментариев в коде.

Подобные средства есть и в C++. Они следуют тем же соглашениям о синтаксисе тегов, что и JavaDoc (см., например, Doc++ или Doxygen).

7 Ссылки

Метки:
Поделиться публикацией
Реклама помогает поддерживать и развивать наши сервисы

Подробнее
Реклама
Комментарии 213
  • +10
    Много спорного, хотя бы о выравнивании и символы указателя/ссылки. Дополнительное именование указателей часто очень выручает, а выделение переменной класса подчеркиванием вместо логичного this. нелепа.
    Хотя конечно каждому своё, кому то наоборот что я перечислил кажется удобным, а называть переменные tHiSiSvArIaBlE удобным.
    • 0
      Да, мне и самому некоторые моменты кажутся спорными (например, пункт № 48). Но документ довольно целостный, как мне кажется.
    • +6
      Не согласен только с 11 пунктом. Очень многие привыкли, что перед скрытыми переменными добавляется 'm' (mDepth), к тому же все сеттеры должны иметь основной параметр с именем value, иначе получается тавтология.

      void setDepth (int value)
      {
      mDepth = value;
      }
      • +6
        Не очень понял, почему заминусовали человека.
        Префикс «m» у скрытых членов класса — достаточно популярный coding style, и многим, включая меня, он кажется удобным. А если Ваша IDE имеет средства автодополнения, то, написав «m», Вы сразу получаете список всех скрытых членов класса. С постфиксом "_" такого не получится, да и, как кажется мне, он куда менее нагляден.

        К слову, по причине использования многими программистами IDE с автодополнениями, я также рекомендую везде писать get/set, т.к. можно просто вбить «get» и получить список всех функции класса, которые возвращают значения.
        • 0
          Собственно, ниже в комментах на это тоже указали (раз, два, три).
          • 0
            Это изначально спорный вопрос – сколько людей, столько и мнений. Если посмотреть проекты на github, то много кто использует именно такую нотацию (например вот, библиотека folly от facebook).
            • +3
              Я не спорю с этим. Например, Скотт Майерс тоже пишет с постфиксом "_". Другое дело, что использование префиксов выглядит куда логичнее — это первые буквы при наборе переменной, а, значит, во всяких списках переменных классов они будут удобно расположены и отсортированы в одном месте. Можно использовать _member, можно mMember, можно m_member, но во всех случаях первый символ у Вас однозначно будет указывать на внутренний член класса. Это правда дико удобно.
              • +1
                _member нельзя использовать — подчеркивание в начале слово зарезервивровано для компилятора… А для списка всех членов в текстовом редакторе открывается второе окно с описанием класса. Никто из динозавров IDE не использует.
                • 0
                  Вроде бы зарезервированы идентификаторы, начинающиеся с двух подчёркиваний или подчёркивания и заглавной буквы. В boost подчёркивание ставят в конце, т. е. member_.
                  • 0
                    Зарезервированы идентификаторы:
                    * содержащие два подчёркивания подряд где угодно,
                    * начинающиеся с подчёркивания и большой буквы,
                    * в глобальном namespace — начинающиеся с подчёркивания,
                    * имена из стандартной библиотеки.
                    • 0
                      Ну да, получается, что _member — вполне себе адекватное имя для члена класса, ибо не global namespace.
                • +2
                  Я не сиплюсплюсник, но есть некоторые возражения. Первые символы действительно важны для группировки. Они отражают то, как мыслит программист. И хотелось бы, чтобы мысли вращались вокруг предметной области, а не реализации. Т.е. важнее то, что значит переменная, а не то, где она находится и ее тип.

                  А вообще, очень бы неплохо было бы, чтобы IDE поддерживали не просто выпадающий список, но и «кнопки» для группировки. Т.е. выпадающий список по умолчанию, но сверху (снизу) тулбар, с «private», «static» и т.д.
          • +1
            Никак не могу уловить суть рекомендации 93. Комментарии следует располагать так, чтобы они относились к тому, что они описывают.
            Мне кажется, что в примере оба варианта (рекомендуемый и не рекомендуемый) одинаковы. Или я чего-то не замечаю…
            • +1
              Обратите внимание на вторую строку.
              • +5
                Спасибо, действительно не заметил отступ… Хотя смысл рекомендации для меня очевиднее не стал.

                Кстати, некоторые рекомендации можно было бы поправить с учетом С++11, например:
                — 31. Константы в перечислениях могут иметь префикс — общее имя типа: используйте enum class;
                — 66. Константы с плавающей точкой следует записывать с десятичной точкой и с указанием по крайней мере одной цифры после запятой: используйте литералы;
                — 70. Следует использовать «0» вместо «NULL»: используйте nullptr.
                • 0
                  Документ 2011 года, но редактировать я его не стал. Сам пользуюсь теми вариантами, которые указаны Вами.
                  • –1
                    Учусь в институте, на c++ только лабы и курсовые. Всегда думал, что правильнее использовать NULL, ибо 0 — цифра, NULL — скорее пустота. Даже в небольших лабах это дает повышение читабельности, зачем нужно использовать 0, если используется не c++11?
                    • 0
                      0 — C++98, C++03. В C++11 тоже можно 0, но лучше nullptr.
                      • +2
                        То что можно и видимо нужно — я понимаю. Не понимаю почему, ведь 0 — цифра, а NULL — пустота. В целом вопрос — почему нужно использовать 0, когда NULL понятнее?
                        PS то есть
                        for(int i=0; i<n; i++){}//  ясно
                        node *temp = NULL// вот тут явно NULL яснее смотрится чем 0?
                        
                        • +3
                          Дело в том, что NULL в C++ должен быть определён как
                          #define NULL 0
                          


                          Определять NULL как (void *)0 нельзя. Поэтому следующий код является корректным:
                          f(int x);
                          
                          void g() {
                            f(NULL);
                          }
                          


                          Я согласен что это неинтуитивно. Что именно использовать в C++03 — NULL или 0 — определяется стилем кодирования в каждом проекте, так как саму проблему с null pointer constant в C++03 не решает.
                          • –4
                            А почему код должен быть не корректен? Срабатывает приведение типов — NULL -> int(0).
                            Просто всегда бесило в коде знакомых студентов
                            Foo *a = new Foo();
                            // код код
                            if(a == 0)
                            

                            Ну как указатель можно сравнивать с нулем? Это же логически не верно, пусть даже NULL на самом деле — 0.
                            • 0
                              > Срабатывает приведение типов — NULL -> int(0).

                              Нет такого неявного приведения типов в C++: T* не приводится неявно к int.

                              void f(int x);
                              
                              void g() {
                                int *p = 0;
                                f(p); // compile error
                              }
                              
                              • –2
                                В примере выше ваша функция принимала переменную, а не указатель. Мое, если можно так сказать возмущение как раз и было в том, что для чисел — использовать числа, а для указания пустоты — строки, или указателя, или возможно еще чего-то о чем я не знаю — NULL
                                • 0
                                  Вы, кажется, не предвидели всей полноты последствий:
                                  $ cat zz.cc
                                  #include <iostream>
                                  #include <stddef.h>
                                  
                                  void f(int *x) { std::cout << "int *x" << std::endl; }
                                  void f(int x)  { std::cout << "int x" << std::endl; }
                                  
                                  int main() {
                                    f(0);
                                  }
                                  


                                  Overload resolution выбирает f(int) для NULL (если #define NULL 0), точно также как и для 0.
                                  • –1
                                    Конечно выбирает, ведь 0 === (int)0, а NULL = либо 0, а может nullptr
                                    Если изменить f(0) на f(NULL) — компиляторы не сообразят какую из функций выбрать.
                                    • 0
                                      Это потому что в компиляторах именно из-за этого примера есть костыль, который не совместим с C++03. В C++03 нужно
                                      #define NULL 0
                                      


                                      Однако в GCC и Clang:
                                      #define NULL __null
                                      


                                      И дальше компилятор разбирается по правилам, которых нет в языке.
                              • 0
                                А как же тогда работает код у ваших студентов? Дело в том, что integral constant expression, равный нулю, является null pointer constant и может быть преобразован к T*.

                                void f(int *x);
                                
                                void g() {
                                  f(2 * 21 - 42); // OK
                                }
                                
                                • –1
                                  А что там должно не работать? Собственно ваш пример должен бы работать, в функцию же отправляется результат выражения, или я чего-то не понимаю?
                                  А используется такое в переборе указателей в списках, деревьях и тд
                                  • +1
                                    То есть вы считаете нормальным, что f(2*24 — 42) компилируется, а f(2*42-43) — нет?
                                    • –1
                                      Конечно нет! И потому не согласен с тем, что NULL можно заменять в коде 0.
                                      Что и как реализовано в языке в действительности — один вопрос. Области видимости в классах — важен только на этапе компиляции, однако позволяет не натворить глупостей.
                                      И еще раз — потому надо разделять NULL и 0.
                                      • +1
                                        > Конечно нет! И потому не согласен с тем, что NULL можно заменять в коде 0.

                                        Но NULL проблемы не решает, а словить пару часов интереснейшей отладки overload resolution можно элементарно. Если же там был бы написан ноль, было бы всё понятно.

                                        И вообще, это сегодня неактуально. nullptr.
                                    • 0
                                      Да, пример использования в студию.
                                      • –1
                                        Кода других студентов под рукой нет — вот мое бинарное дерево. Заменив NULL на 0 простой заменой по файлу вроде никаких изменений нет, кроме утечек памяти, которые судя по всему и так должны быть. Надо бы исправить.
                                        Скрытый текст
                                        #include <iostream>
                                        #include <fstream>
                                        #include <string>
                                        using namespace std;
                                        
                                        class Product{
                                        private:
                                        
                                            int num;
                                            // string name;
                                            // string supplier;
                                            // int count;
                                            // string date;
                                        
                                        public:
                                        
                                            void input(){
                                                // cout<<"Product:            "; getline(cin, name);
                                                // cout<<"Supplier:           "; getline(cin, supplier);
                                                // cout<<"Date:               "; getline(cin, date);
                                                // cout<<"Amount:             "; cin>>count;
                                                cout<<"Number of delivery: "; cin>>num;
                                            }
                                            static Product getkey(){
                                                Product key;
                                                cout<<"Key: ";
                                                cin>>key.num;
                                                return key;
                                            }
                                            void print(){
                                                cout<<"Number of delivery: "<<num<<"\n";
                                                // cout<<"Product:            "<<name<<"\n";
                                                // cout<<"Supplier:           "<<supplier<<"\n";
                                                // cout<<"Amount:             "<<count<<"\n";
                                                // cout<<"Date:               "<<date<<"\n";
                                            }
                                            void output(ostream &stream){
                                                stream<<num;
                                            }
                                            friend bool operator <(Product const a, Product const b){
                                                return a.num < b.num;
                                            }
                                        
                                            friend bool operator ==(Product const a, Product const b){
                                                return a.num == b.num;
                                            }
                                        };
                                        
                                        class node
                                        {
                                        public:
                                            Product data;
                                            node *left,
                                                 *right;
                                        
                                            void output(ostream &stream){
                                                if(this != NULL){
                                                    left->output(stream);
                                                    data.output(stream);
                                                    stream<<" ";
                                                    right->output(stream);
                                                }
                                            }
                                            node(Product var){
                                                data = var;
                                                left = right = NULL;
                                            }
                                            void remove(){
                                                if(this != NULL){
                                                    left->remove();
                                                    right->remove();
                                                    delete left;
                                                    delete right;
                                                }
                                            }
                                            ~node(){
                                                this->remove();
                                            }
                                            void output_sheets(){
                                                if(this != NULL){
                                                    if(left == NULL && right == NULL){
                                                        data.print();
                                                    }else{
                                                        left->output_sheets();
                                                        right->output_sheets();
                                                    }
                                                }
                                            }
                                        };
                                        
                                        class bintree
                                        {
                                        private:
                                            node *root;
                                        public:
                                            bintree(){
                                                root = NULL;
                                            }
                                            bool add(){
                                                Product new_data;
                                                new_data.input();
                                        
                                                if(root == NULL){
                                                    root = new node(new_data);
                                                    return true;
                                                }
                                                node *elem = root,
                                                                *parent = NULL;
                                                bool isLeft;
                                                while(elem != NULL){
                                                    if(new_data == elem->data){
                                                        return false;
                                                    }
                                                    isLeft = new_data < elem->data;
                                                    parent = elem;
                                                    if(isLeft){// если новый элемент меньше корня
                                                        elem = elem->left;
                                                    }else{
                                                        elem = elem->right;
                                                    }
                                                }
                                                if(isLeft){
                                                    parent->left = new node(new_data);
                                                }else{
                                                    parent->right = new node(new_data);
                                                }
                                                return true;
                                            }
                                        
                                            void output(ostream &stream){
                                                root->output(stream);
                                            }
                                            void remove_max(){
                                                node *elem = root,
                                                     *prev = NULL;
                                                while(elem->right != NULL){
                                                    prev = elem;
                                                    elem = elem->right;
                                                }
                                                if(elem == root){
                                                    root = root->left;
                                                }else{
                                                    prev->right = NULL;
                                                }
                                                delete elem;
                                            }
                                        
                                            void output_sheets(){
                                                root->output_sheets();
                                            }
                                        
                                            Product find(){
                                                Product key = Product::getkey();
                                                node *elem = root;
                                                while(elem != NULL){
                                                    if(key == elem->data){
                                                        return elem->data;
                                                    }else if(key < elem->data){// если новый элемент меньше корня
                                                        elem = elem->left;
                                                    }else{
                                                        elem = elem->right;
                                                    }
                                                }
                                                return Product();
                                            }
                                        };
                                        
                                        void clear(){
                                            cout<<"\n";
                                            for(int i=0; i<30; i++) cout<<"-";
                                            cout<<"\n";
                                        }
                                        void menu(){
                                            clear();
                                            cout<<"1) Add node\n";
                                            cout<<"2) Find node by key\n";
                                            cout<<"3) Output tree\n";
                                            cout<<"4) Remove node with max key\n";
                                            cout<<"5) Output tree's sheets\n";
                                            cout<<"6) Output in file\n";
                                            cout<<"0) Quit\n";
                                            cout<<"Your choise: ";
                                        }
                                        int main()
                                        {
                                            bintree tree;
                                            int ch;
                                            while(true)
                                            {
                                                menu();
                                                cin>>ch;
                                                switch(ch)
                                                {
                                                    case 1:
                                                        if(!tree.add()){
                                                            cout<<"Element with this key exsist";
                                                        }else{
                                                            cout<<"Element succesfully added";
                                                        }
                                                        break;
                                                    case 2:
                                                        tree.find().print();
                                                        break;
                                                    case 3:
                                                        tree.output(cout);
                                                        break;
                                                    case 4:
                                                        tree.remove_max();
                                                        break;
                                                    case 5:
                                                        tree.output_sheets();
                                                        break;
                                                    case 6:{
                                                        char filename[50];
                                                        cout<<"Filename: ";
                                                        cin>>filename;
                                                        ofstream filestr(filename);
                                                        if (filestr.is_open()){
                                                            tree.output(filestr);
                                                            filestr.close();
                                                        }else{
                                                            cout << "Error opening file";
                                                        }
                                                    }
                                                    break;
                                                    case 0:
                                                        return 0;
                                                        break;
                                                }
                                            }
                                        
                                        }
                                        

                                        • 0
                                          Я не вижу где вы здесь использовали нетривиальный integral constant expression, равный нулю, в качестве null pointer constant.
                                          • –1
                                            Потому что я, что возможно в корне не верно — не стал перебирать весь список за добавлением нового элемента, а храню в объекте ссылки на последний и первый элемент, мне это не требуется. Реализация именно в функции добавления, что-то вроде
                                            node *temp = root,
                                                 *prev = 0;
                                            while(temp!=0){
                                                prev = temp;
                                                temp = temp->next;
                                            }
                                            prev->next = new node();
                                            
                                            • 0
                                              И? Вы же написали temp!=0, а не temp!=1-1.
                                              • +1
                                                Да, я нигде не писал что кто-то придумывает выражения для проверок. Я написал что сравнение указателя и числа выглядит странно и не логично, как и присваивание.
                                                • 0
                                                  Так, стоп. Было сказано следующее:

                                                  gribozavr:
                                                  > f(2 * 21 — 42); // OK

                                                  На что вы ответили:
                                                  > А используется такое в переборе указателей в списках, деревьях и тд
                                                  • 0
                                                    Мы с вами видимо запутались, это был ответ на это:
                                                    gribozavr:
                                                    > А как же тогда работает код у ваших студентов?
                                                    PS Хотя судя по всему я просто не совсем правильно ответил, но относилось именно к этому.
                                          • +1
                                            > if(this != NULL){

                                            Epic. Так не бывает.
                                            • –2
                                              Я долго сомневался прежде чем так писать. Однако лабу приняли, все ок. То есть компилятор сам не отправит пустой объект в функцию?
                                              • 0
                                                При чём тут компилятор? Вызывать функции-члены если у вас нет живого объекта — undefined behavior в момент вызова.
                                              • –1
                                                Вообщето такое возможно но мало вероятно.
                                                SomeClass *lv_Obj = 0; lv_Obj->DoSmth(); // функция вызовется и если внутри нет обращения к полям то ещё и успешно выполнится.

                                                но вообще так писать разуметься не хорошо, а если уж и пишешь то следует писать
                                                if (!this) return;

                                                ибо ненужные скоупы это тоже плохо.
                                                • +2
                                                  Это UB в момент вызова.
                                                  • 0
                                                    даже не поморщится
                                                    liveworkspace.org/code/XjE1O$0
                                                    Вызвать можно, но это вызовет ошибку в случае если есть обращение к полям, виртуальным методам, или если сам метод является виртуальным (последние не совсем так но проблемы могут возникнуть).
                                                    • +6
                                                      Разыменование нулевого указателя — неопределенное поведение. А то, что оно не вызвало ошибку — это один из вариантов этого поведения исключительно по воскресеньям.
                                    • 0
                                      while (1) { // НЕТ!
                                        :
                                      }
                                      

                                      Вот, кстати пример из статьи. Все знают, что while работает только с булевыми значениями, а значит все остальное внутри скобок приводится к булевому значению. Также все знают, что 1 == true.
                                      Так почему же зная то, что NULL = 0 — стоит писать 0, а в циклах такая запись не естественна?
                                      • 0
                                        См. ответ выше. Проблема в неинтуитивном определении null pointer constant в C++03.
                                        • –2
                                          В С++ нет bool это иллюзия есть лишь 0 и не 0 так что там ничего не приводится.
                                          Так почему же зная то, что NULL = 0 — стоит писать 0

                                          как минимум потому что NULL является макросом который можно переопределить.
                                          а в циклах такая запись не естественна?

                                          с циклами я кстати с автором тоже несогласен while (1) гораздо лучше
                                          #define true 0 // happy debugging motherfucker!
                                          • 0
                                            И? Области видимости тоже можно переопределить, только зачем?
                                            • –1
                                              Что и? проблема в том что это можно сделать, и это может повлечь не очевидную проблему (злая шутка)
                                              Поэтому писать 0 всё же лучше, а с точки зрения читаемости это ничего не поменяет.
                                              • 0
                                                Это нельзя делать. UB.
                                                • –1
                                                  Ты о чём? писать 0 вместо NULL это UB? мда…
                                                  • 0
                                                    Нельзя делать #define NULL whatever в пользовательском коде.
                                                    • –1
                                                      даже не поморщится
                                                      http://liveworkspace.org/code/XjE1O$0
                                                      Вызвать можно, но это вызовет ошибку в случае если есть обращение к полям, виртуальным методам, или если сам метод является виртуальным (последние не совсем так но проблемы могут возникнуть).
                                                      • +2
                                                        Вы не знаете что такое UB, а спорите.
                                                        • –3
                                                          в данном случае это не неопределённое поведение, и если бы вы знали как устроены методы в языке С++ вы бы не говорили этой глупости.
                                                          • +4
                                                            Методы в языке С++ никак не устроены. Внутреннее устройство методов не регламинтируется стандартом. Зато стандартом оговаривается, что разыменование 0-го указателя — UB.
                                                              • –3
                                                                Да про раз именовывание я как-то забыл, действительно, прошу прощения. Просто зациклился на том что метод это функция со скрытым параметром this
                                                          • –3
                                                            Ответил до редактирования.
                                                            Нельзя делать #define NULL whatever в пользовательском коде.

                                                            Можно, другое дело что это очень плохо, но тем не менее сделать это можно.
                                                            • +2
                                                              Нельзя переопределять имена из стандартной библиотеки. Почитайте что такое UB, узнаете много нового.
                                                              • –3
                                                                Нельзя переопределять имена из стандартной библиотеки. Почитайте что такое UB, узнаете много нового.

                                                                я не говорил что это не UB, несомненно это UB но это не значит что этого нельзя сделать
                                                                http://liveworkspace.org/code/XjE1O$2
                                                                • +4
                                                                  И какой в этом смысл? Ввести UB в свою программу для лулзов?
                                                                  • –4
                                                                    Ну в универе мы так часто прикалывались друг над другом, от туда и появилась устойчивая привычка использовать 0 вместо NULL.
                                                                    • +3
                                                                      Это действительно убедительная мотивация пункта рекомендаций стиля программирования от профессионала, не поспоришь.
                                                                      • –1
                                                                        Ну хз я стараюсь предвидеть любые ситуации даже такие незначительные, а что писать 0 вместо NULL это плохо? Честно говоря везде где я работал писали 0, и никаких проблем никогда не было. Может у вас есть история получше?
                                                                        • +1
                                                                          Это плохая мотивация, независимо от того, какой вариант на самом деле лучше. А вдруг кто-то (совершенно без UB) сделает #define print zzz, а у вас куча функций так называется? От этого тоже будете «защищаться»?

                                                                          Моё мнение выражено выше. NULL или 0 — разницы нет, так как *проблему* это не решает.

                                                                          habrahabr.ru/post/172091/#comment_6046361
                                                              • +3
                                                                Нельзя делать #define NULL whatever в пользовательском коде.
                                                                Можно, другое дело что это очень плохо, но тем не менее сделать это можно.

                                                                Технически, это возможно, вы правы. Компиллятор даже возможно скомпиллирует вашу программу. Но никто не сможет предсказать, как она будет рабоать. Потому что, используяю такой define, вы нарушили правила игры. Теперь ваша программа — уже не валидная программа на языке С++.
                                                        • +1
                                                          Вопрос был из читабельности. 0 — число. NULL — пустота, для чисел является нулем.
                                                          Если кто-то может зло пошутить — это не повод писать не понятно.
                                                          • 0
                                                            А кому непонятно? Новичкам разве что, но гадлайны, делаются для разработок в компаниях куда не берут людей которые не знают языка. Обычно за
                                                            if(ptr != 0) по рукам бьют т.к. это равносильно if(SomeBool == true) конечно компилятор это разрулит, но обычно это заставляет обращать лишнее внимание на условие.
                                                            • +1
                                                              if(SomeBool == true) — подобное, как мне кажется, надо использовать только в языках с нестрогой типизацией и не ==, а ===, но это другая тема и тут я с вами согласен.
                                                              А за foo *bar = 0 (представим что у нас еще нет nullptr) по рукам не бьют?
                                                              • –1
                                                                А за foo *bar = 0 (представим что у нас еще нет nullptr) по рукам не бьют?
                                                                Нет в С++ это корректное выражение которое понимает каждый.
                                                                • 0
                                                                  Эх… Вот вырасту и буду бить. По мне — так это не логично. Уже 25 раз описал почему.
                                                      • +1
                                                        operator bool bool() const значит не существует?
                                                • 0
                                                  Вопрос был «зачем использовать 0?»

                                                  Где то читал, зачем и почему, но забылось уже, так что присоединюсь к вопросу.
                                                • +1
                                                  0 — присутствие отсутствия, NULL — отсутствие присутствия ©
                                          • –7
                                            Была одна очень хорошая рекомендация по именованию локальных переменных vs членов классов.
                                            Локальные some_var, члены классов как somеVar. Тогда неажно насколько большое полотнище кода надо изучать/редактировать, всегда с первого взгляда понятно с чем работаем — локальная или член класса.
                                            • +11
                                              Если это не указано явно, C++ считает, что возвращаемое значение имеет тип int.

                                              А мне казалось, он как раз это запрещает (если не крутить некоторые флаги, по крайней мере).

                                              51. Символ указателя или ссылки в языке C++ следует ставить сразу после имени типа, а не с именем переменной.

                                              Мне такой способ тоже больше нравится, но он имеет одну проблему.
                                              int* ptr, not_ptr;
                                              int *ptr, not_ptr;
                                              

                                              Второй вариант более очевидный. Хотя оба плохие. :)
                                              • 0
                                                Угу. Мне тоже куда больше нравится, когда звёздочка в объявлении указателя «клеится» к типу, нежели к имени переменной.
                                                Однако, при этом я следую ещё одному правилу: каждая инициализация должна требовать отдельной строки для себя.
                                                Странно, что в статье про это ничего не говорится (если только я это пропустил).

                                                А в целом, статья хорошая.
                                                Хоть и не со всеми пунктами согласен — но лучше кодить по жёстко утверждённому стиль, чем вообще без оного.
                                                • 0
                                                  Второй вариант однозначно более очевиден, особенно, когда объявляются несколько переменных через запятую.
                                                  Глядя на первый вариант может показаться, что объявлено два указателя на переменные целого типа. Хотя с точки зрения компилятора C++ в обоих случаях указателем является только ptr, а not_ptr просто переменная целого типа.
                                                  Если объявляем много указателей, например:

                                                  Stream *Stream1, *Stream2, *Stream3;
                                                  

                                                  то второй вариант не просто предпочтительнее, а единственно логичный.

                                                  А в таком примере уже не работает правило 12:

                                                  void setStream(Stream *Stream1, Stream *Stream2, Stream *Stream3);
                                                  
                                                • +14
                                                  Рекоммендация #0: используйте любой, подходящий вам и команде стиль, но в рамках одного проекта других стилей быть не должно и придерживаться его надо обязательно.
                                                  • +2
                                                    Ещё хочу написать кое-что.

                                                    1. Любой символ, заданный в тексте программы своим кодом, задокументировать в свободной форме.

                                                    s += L'\u2212'     // 2212 = minus sign
                                                    

                                                    2. Специфично для Delphi/Builder. Любая ссылка на конкретный объект VCL, в тексте, в форме или в виде уникального события — дай ему говорящее имя. Для «массовых» событий, общих для нескольких компонентов с определением компонента по dynamic_cast<TButton*>(Sender)->Tag, это не требуется, однако само событие должно иметь говорящее имя.

                                                    3. Если у метода есть аналог из известной библиотеки, подстраиваться под неё. Например: const char* Printf::c_str(), но не cStr().
                                                    • +4
                                                      Далее.

                                                      4. Венгерская запись — не именование типа (в IDE уже 15 лет есть навигация по коду), а сокращение идентификаторов. Например, EPreprocess = PreprocessException, nUsers = UserCount.

                                                      5. Различать id (суррогатный код), index (порядковый номер), count (кол-во объектов, которые компьютер различает), quantity (кол-во объектов, которые компьютер не различает) и number (что либо из реальной жизни, так называющееся). Например: carId — идентификационный код, отличающий машину от других, carIndex/iCar — номер машины из n шт., carCount/nCars — кол-во машин в гонке, carQty — кол-во машин в продаже, carNo — номер машины (государственный или стартовый).
                                                    • +1
                                                      Не без замечаний, но в целом годно.
                                                      К п. 41: Опыт подсказывает, что в .cpp первым инклюдом удобно включать собственный .h-файл. Этим проверяется, что он самодостаточен, т. е. не зависим от предыдущих инклюдов в .cpp.
                                                      • 0
                                                        С этим согласен, сам постоянно так делаю.
                                                        Гораздо хуже, когда твоя библиотека в другом проекте будет требовать библиотеки, которые не подключены.
                                                      • +16
                                                        Много уже неактуального, например про 80 колонок или символы табуляции, не говоря уже об игнорировании актуального C++11.

                                                        Есть вредные советы. Из того, что еще не упоминались, например 8. Следует называть имена типов в шаблонах одной заглавной буквой. А кто угадает, что означает каждый аргумент:
                                                        template<
                                                            class K,
                                                            class T,
                                                            class H,
                                                            class E,
                                                            class A
                                                        > class unordered_map;
                                                        


                                                        А вот так это выглядит в оригинале:
                                                        template<
                                                            class Key,
                                                            class T,
                                                            class Hash,
                                                            class KeyEqual,
                                                            class Allocator
                                                        > class unordered_map;
                                                        



                                                        Отдельная история с именованием переменных. Предложенная здесь система похожа на использующуюся, например, в Qt. Microsoft используют немного другугю системму наименований, а вот standard C++ Library и boost вообще использует большинство имен в нижнем регистре с подчеркиванием. Тут может быть только один совет — именуйте так, как принто в той библиотеке, которую вы используете больше всего.

                                                        И      еще,   я            не       могу 
                                                        понять почему выравнивание улучшает читаемость
                                                        
                                                        • +1
                                                          Если в проекте все строчки не длиннее 80 (или 100) колонок, удобно открывать 2 файла параллельно.

                                                          С++ похоже один из не многих языков, где так и не смогли окончательно решить, как писать переменные :)

                                                          Ваш пример выравнивания, конечно, читать неудобно. Чаще оно используется немного по-другому. Мне кажется, есть 2 типа людей: 1) те, кому субъективно нравится выравнивание, они часто его используют; 2) остальные выравнивание практически или совсем не используют. Все при этом руководствуются только улучшением читаемости.
                                                          • +1
                                                            Я сам стараюсь не вылазить за сотню, но 80 — все-таки уже перебор.

                                                            А вот на счет выравнивания, тот пример, который приведен в статье, мне кажется не многим лучше. Ну да, я вижу паттерн, но у меня от него только в глазах рябит. Читаю же я все равно слева направо, и что там в других строках — мало интересует. В типографике наоборт, стараются избавляться от коридоров.
                                                            • +2
                                                              Текст на естественном языке читается по-порядку, всплошную, в то время как в исходном тексте структура имеет огромное значение.
                                                              • –1
                                                                Вообще не понимаю, кому в голову может придти использовать выравнивание в типичном проекте, как показывает практика выравнивание производится один раз, при написании кода, а со временем все это превращается в фарш после переименований классов и никем не поддерживается. А при каждом переименовании просматривать и переформатировать n кусков кода — слишком дорогое удовольствие. Правда автоматическое выравнивание инструментальными средствами наверное решает проблему, но сталкиваться не приходилось.
                                                              • –1
                                                                100-120 тоже нормально открываются на современных FullHD и больше мониторах. А то писать из-за 80 символов постоянно вот так:
                                                                for (int tableNo = 0; tableNo < nTables;
                                                                     tableNo += tableStep) {
                                                                  ...
                                                                }

                                                                не очень удобно.
                                                                • –2
                                                                  Если в коде много строк, которые не влезают 80 символов, возможно с кодом что-то не так. Поясните, к примеру, чем оправдано использование название tableNo вместо обычного i (j, k и т.д)?
                                                                  • +2
                                                                    Я скопировал это из статьи, совет 39. Без понятия, откуда у автора этот код.

                                                                    Например,
                                                                    animState->setTimePosition(std::min(anim->getLength(), animState->getTimePosition())); 

                                                                    86 символов + отступы.
                                                                    • +1
                                                                      ИМХО, в таких случаях лучше переносить. Более того, если что-то не вмещается в строку, лучше разбить его на логические составляющие.

                                                                      animState->setTimePosition(
                                                                          std::min(
                                                                              anim->getLength(),
                                                                              animState->getTimePosition()
                                                                          )
                                                                      );
                                                                      

                                                                      • 0
                                                                        Это стандартный капитанский совет людей, кто все вмещает в 80 символов. Только вот от этого читать ее нифига не стало лучше, а как по мне — наоборот.
                                                                        И это функция с одним параметром.

                                                                        Потом, самое неприятное — это переносить явно однострочные функции.
                                                                        deltaTransform.translate = srcBone->getInitialPosition() - dstBone->getInitialPosition();
                                                                        deltaTransform.rotate = dstBone->getInitialOrientation().Inverse() * srcBone->getInitialOrientation();
                                                                        deltaTransform.scale = srcBone->getInitialScale() / dstBone->getInitialScale();


                                                                        Замечу, что я не выступаю против переносов. Естественно, когда строка не вмещается в разумных границах монитора, программиста хочется ругать. Дело просто в том, что 80 строк правда далеко не всегда хватает, а городить перенос на каждом чихе — бесит. 100-120 столбцов — самое то. Лично у меня стоит ограничение на 110 символов, я спокойно отрываю два файла на одном мониторе FullHD.
                                                                        • 0
                                                                          Читать стало лучше, потому что глаз видит больше. Газетные колонки по этой причине существуют. Еще в программировании опасны паттерны, потому что глаз замыливается и можно перепутать X с Y, например.
                                                                          Еще это удобнее читать в редакторе, в котором открыто 6 вкладок, по ssh, на телефоне и нетбуке, на гитхабе. 80 символов — самое то.
                                                                          • 0
                                                                            Видите, Вам лучше, а мне нет. Очень все индивидуально.
                                                                            Соглашусь, что в крупных открытых проектах лучше делать все по 80 символов, ибо мы не знаем, кто этот проект будет читать. Внутрикорпоративно же я приблизительно знаю, каким оборудованием владеет народ, и делать такие никому ненужные ограничения смысла не вижу.
                                                                            • 0
                                                                              Я бы не сказал, что все индивидуально. Поле зрения у глаза круглое, поэтому длинные строки нужно читать, а «квадрат» можно охватить взглядом сразу. Хотя насчет 80 я погорячился, пожалуй.
                                                                              • 0
                                                                                При беглом анализе — да, скорее, Вы правы.
                                                                                А вот если нужно вчитываться в написанное, то при сильно скомканном коде (как в примере выше), приходится включать мозговой парсер. Строка же принимается также, как строка в обычной книжке.
                                                                            • 0
                                                                              И да, если приспичить сделать все по феншую, для VS есть плагин StyleManager, который можно настроить на любой стиль и длину строк. Есть и свободные аналоги, но названия не помню.
                                                                      • 0
                                                                        Вот по поводу последнего — вы погорячились.

                                                                        Видите вы строчку внутри вложенного цикла:
                                                                        var tratata = MumbleThis(i, j);
                                                                        

                                                                        Неужели она лучше чем:
                                                                        var tratata = MumbleThis(year, dayOfYear);
                                                                        


                                                                        Индексы i,j — допустимы только когда 100% кристально понятно, что они обозначают и невозможно разночтений или необходимости вникать.
                                                                        • +2
                                                                          Я бы сказал, что они допустимы только когда это абстрактные индексы, не имеющие воплощения в предметной области (или имеющие именно в таком виде, например в математической формуле именно i используется). Потому что-то, что кристально понятно сейчас, пока тело цикла из одной строки состоит, потом может уже не быть понятным вообще, особенно если десяток строк добавить. В эту же степь неявные преобразования «физических» номеров (начинаются с 1) в «абстрактные» индексы (с 0) типа a[month-1] = 0; Сиди и гадай потом, то ли это преобразование в индекс, то ли какая-то логика про предшествующий месяц.
                                                                          • 0
                                                                            Соглашусь, верное замечание.
                                                                  • +1
                                                                    Крайне неприятно, когда в исходном коде попадаются строки по 150-200 колонок, а то и больше, особенно когда кроме IDE открыто ещё что-нибудь, и нужно уменьшить ширину окна, или когда код просматриваешь не в IDE, а в текстовом редакторе.
                                                                    Что касается выравнивания, читаемость оно улучшает, но в довольно специфических случаях: при задании значений двумерным массивам, например.
                                                                    • +1
                                                                      Выравнивание улучшает читаемость при использовании случае выравнивания однотипных (в данном контексте) сущностей. Прекращение выравнивания означает, что пошли сущности другого типа.
                                                                      • 0
                                                                        Не проще ли для таких случаев использовать разделитель в виде пустой строки?
                                                                        • 0
                                                                          Обычно он используется для разделения логических блоков. Могут быть неоднозначности.
                                                                    • +28
                                                                      Вкратце: всё пишем будто на джаве.
                                                                      • +2
                                                                        На каком стиле кодирования основана статья? Их же бесчисленное множество: habrahabr.ru/post/116819/
                                                                        • +16
                                                                          А кто такие эти geosoft.no чтобы давать рекомендации по стилю C++? У них рекомендаций больше, чем написанных программ. Я вижу у них на сайте только ряд java-библиотек. Как объяснить программистам, что им теперь нужно писать по указке geosoft?

                                                                          Есть куча нормальных стандартов, написанных реальными разработчиками, а не Wannabe Styleguide Writer-ами.
                                                                          Google C++ Style Guide
                                                                          Mozilla Coding Style
                                                                          Linux kernel coding style
                                                                          Microsoft Design Guidelines for Class Library Developers
                                                                          Boost Guidelines
                                                                          Alexandrescu C++ Coding Standards
                                                                          • НЛО прилетело и опубликовало эту надпись здесь
                                                                            • НЛО прилетело и опубликовало эту надпись здесь
                                                                              • –1
                                                                                Это все так, но добавление подчеркивания к имени членов класса позволяет легко отличить их от локальных переменных и позволяет избежать конфликтов имен. А кодирование типа данных в имени это действительно архаизм времен убогих IDE
                                                                              • +1
                                                                                48 Про глобальные функции не согласен.
                                                                                49 Не согласен, что всегда все члены класса следует объявлять как private и писать к ним get/set. Чаще — да, всегда — нет.
                                                                                58 Да ладно?
                                                                                70 Следовало бы и про nullptr сказать.
                                                                                51, 72-83 холиварные пункты. Считаю, лучше было бы их убрать, либо написать более демократично.
                                                                                • 0
                                                                                  Добавил в избранное, хотя тут годных советов максимум процентов 30-40. Остальные — спорные, холиварные, вкус фломастера (на вкус и цвет фломастеры разные).
                                                                                  • +3
                                                                                    В статье больше спорных советов, чем полезных. В дополнение к критике выше:

                                                                                    Все определения должны находиться в файлах исходного кода.

                                                                                    class MyClass
                                                                                    {
                                                                                    public:
                                                                                      int getValue () {return value_;}  // НЕЛЬЗЯ!
                                                                                    


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

                                                                                        llvm.org/devmtg/2012-11/Carruth-OptimizingAbstractions.pdf
                                                                                        llvm.org/devmtg/2012-11/videos/Carruth-OptimizingAbstractions.mp4
                                                                                        • 0
                                                                                          Все это становится полезным только для 20% кода, работающего под нагрузкой. Если же ваш геттер вызывается 3 с половиной раза в сутки, как это чаще всего и бывает, то никакого улучшения производительности вы не заметите. Зато заметите, что
                                                                                          — теперь вам нужно перекомпилировать все зависимые модули при изменении одного геттера.
                                                                                          — не может идти речь ни о какой бинарной совместимости. Вы просто не можете взять и поменять реализацию, не поменяв интерфейс.
                                                                                          • +2
                                                                                            Если вы пишете типичное энтерпрайз GUI приложение, пожирающее мегатонны RAM, в которое меланхоличный пользователь раз в минуту тыкает мышкой и затем размышляет «а что это оно мне написало», то ваш подход оправдан. Я работаю в несколько другой области.
                                                                                            • –1
                                                                                              Вы знаете, я просто смотрел код под профиллировщиком и видел, что тормозит. Всего один раз, после того как я уже устранил 3 или 4 основных затыка, у меня производительность уперлась в цепочку из 5 последовательных вызова виртуальных функций.
                                                                                              • +4
                                                                                                Вот вам простой эксперимент: я скомпилировал Clang в обычном режиме Release+Asserts, и в таком же режиме, но добавив -fno-inline -fno-inline-functions. Измерялось время исполнения тестов (CPU time).

                                                                                                R+A: 12m40.520s
                                                                                                R+A no-inline: 22m29.980s

                                                                                                Выводы о пользе инлайнинга делайте сами.
                                                                                                • –1
                                                                                                  Внутри одного модуля трансляции встраивание будет работать даже в случае, если вы не пометили функцию как inline. Если включена межмодульная оптимизация, то встраивание, возможно, будет рабоать и в случае, когда объявление и реализация находятся в разных модулях трансляции. Все это не имеет прямого отношения к определению функции в заголовочном файле. С другой стороны, ваш тест не показывает, сколько процентов кода тормозит.
                                                                                                  • 0
                                                                                                    > Все это не имеет прямого отношения к определению функции в заголовочном файле.

                                                                                                    КО: заголовочный файл становится частью многих единиц трансляции.

                                                                                                    > С другой стороны, ваш тест не показывает, сколько процентов кода тормозит.

                                                                                                    Без инлайнинга код начинает тормозить везде равномерно.
                                                                                                    • 0
                                                                                                      Да, но вы отключили встраивание глобально, там, где оно могло бы сработать, даже если бы определение функции было отделено от объявления, оно теперь не работатет. Точнее, только такие случаи вы и показали. Потому что там, где встраивание не могло срабоать раньше, оно раньше и не срабатывало.

                                                                                                      Без инлайнинга код начинает тормозить везде равномерно.

                                                                                                      Ну вот не факт. Какие-то части кода вызываются чаще, другие — реже. Мне вообще кажется, что основные проблемы были вызваны тем, что перестало срабатывать встраивание в каких-нибудь контейнерах. Или вроде того случая, который вы привели по ссылке в начале обсуждения.
                                                                                                      • 0
                                                                                                        > Да, но вы отключили встраивание глобально

                                                                                                        Да. Что вы дальше хотели сказать я так и не понял. (Обычный R+A компилировался без LTO, если вы это имели ввиду.)

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

                                                                                                        Основные — да. Но не все. Кроме того, мы же обсуждаем «правило» что инлайн функции в классах определять «НЕЛЬЗЯ». Поэтому считаю тест корректным.
                                                                                                        • 0
                                                                                                          Понимаете, вы выключили слишком много inline-ов. При простом переносе определения геттеров в файлы имплементации, выключится существенно меньше inline-ов. Поэтому тест не совсем корректен.

                                                                                                          Кроме того, мы же обсуждаем «правило» что инлайн функции в классах определять «НЕЛЬЗЯ»

                                                                                                          С оговоркой, что если очень хочется, то можно.

                                                                                                          Фактически, я бы сказал, что по умолчанию вы делаете все функции классов невстраиваемыми, если нет явной причины сделать их встраиваемыми.
                                                                                                          • +1
                                                                                                            > При простом переносе определения геттеров в файлы имплементации, выключится существенно меньше inline-ов. Поэтому тест не совсем корректен.

                                                                                                            Не согласен со словом «существенно». Зависит от приложения.

                                                                                                            > Фактически, я бы сказал, что по умолчанию вы делаете все функции классов невстраиваемыми, если нет явной причины сделать их встраиваемыми.

                                                                                                            А вот с этим я согласен.
                                                                                        • +1
                                                                                          Ага, особенно для шаблонов. Впрочем, если в clang осилят нормальные модули, то быть может вообще получится уйти от разделения на h и cpp просто правильно подставляя экспорты и импорты.
                                                                                          • 0
                                                                                            Модули не подразумевают что у вас не будет .h. Они останутся. Модули говорят что у вас не будет #include. В Clang Douglas Gregor идёт дальше и предлагает оставить и #include, но автоматически преобразовывать его в импорт, если .h модульный.

                                                                                            clang.llvm.org/docs/Modules.html
                                                                                      • +20
                                                                                        34, Первый раз слышу про файлы с расширением *.с++ (MyClass.c++), это где так принято именовать?
                                                                                        • +4
                                                                                          Правила явно писал Java кодер, некоторые вещи слишком избыточные, POD структуры вполне себе удобная сущность, да и в приватных классах можно не городить весь огород с сокрытием полей, только читаемость теряется.
                                                                                          Приватные поля лучше записывать через
                                                                                          m_name;  
                                                                                          

                                                                                          Так к ним быстрее из IDE добраться. В общем, Qtшный стандарт как-то ближе и теплее.
                                                                                          • +1
                                                                                            Удивительно прям, почти нет заминусованных комментариев. Обычно такие холиварные топики похожи на поле битвы.
                                                                                            • +15
                                                                                              Все дружно выступают против автора этих рекомендаций.
                                                                                            • 0
                                                                                              ИМХО тут очень, очень много вредных советов.
                                                                                              1. Допускаются любые нарушения рекомендаций, если это улучшает читаемость.

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

                                                                                              XD тогда зачем вообще заводить гайдлайни по коду?
                                                                                              Позволяет легко отличать переменные от типов, предотвращает потенциальные коллизии имён, например: Line line;

                                                                                              Всегда думал что наилучший подход в данном случае использовать префиксы видимости (хоть они и не очень нравятся), они очень хорошо помогают в условиях современных IDE с интелесенсом, и при чтении, сравни:

                                                                                                m_Field = lv_Field + _Offset;
                                                                                              /*
                                                                                              сразу при чтении понятно m_Field - поле класса lv_Field1 - локальная переменная метода _Offset - параметр метода.
                                                                                              при этом нет никаких конфликтов имён, быстро читается + при вводе m_ интелесенс сразу выведет поля также и при lv_ и _
                                                                                              /**/
                                                                                                field = field1 + offset; // что от чего и где совсем непонятно.
                                                                                              


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

                                                                                              Зачем? при этом ведь возможен конфликт имён, и из-за этого тебе потом приходится ставить постфикс в приватных переменных, лучше с большой буквы т.к. это в любом случае будет выглядеть лучше
                                                                                              SomeObj.Method() // гармония SomeObj.method() // дизгармония
                                                                                              7. Названия пространств имён следует записывать в нижнем регистре.

                                                                                              Логика тоже неясна. Зачем дробить понятие наименование на такие группы? когда конфликты в случае namespace, class name, func name. практически никогда не происходят, гораздо лучше задать им единый стиль.
                                                                                              SomeNamespace::SomeObj.Method() // гармония some_namespace::SomeObj.method() // дизгармония
                                                                                              8. Следует называть имена типов в шаблонах одной заглавной буквой.
                                                                                              Это самый вредный совет который только можно было придумать, параметры шаблона, равносильны параметрам функции, тыже небудешь делать в функции такие параметры: void SomeFunc(int A, int C, int B, char* T)
                                                                                              9. Аббревиатуры и сокращения в именах должны записываться в нижнем регистре.

                                                                                              Не имеет смысла аббревиатуры на то и аббревиатуры что имеют определённый стиль написания, и менять его не следует.
                                                                                              11. Членам класса с модификатором private следует присваивать суффикс-подчёркивание.

                                                                                              Суффиксы самая бессмысленная вещь, либо префиксы либо без них. Если приватное имя, и имя параметра будут совпадать, то будет очень легко допустить ошибку при наборе + интелесенс поможет сделать ошибку, а при чтении суффикс _ вообще будет незаметен. Вредный совет.
                                                                                              12. Настраиваемым переменным следует давать то же имя, что и у их типа.

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

                                                                                              SomeFunc(string& stringPath, int CountInt, float OffsetFoloat)

                                                                                              Зачем повторять тип когда IDE сама подскажет?
                                                                                              14. Переменные, имеющие большую область видимости, следует называть длинными именами, имеющие небольшую область видимости — короткими.

                                                                                              название пункта звучит очень гупо. Надо было сказать про стандартное допущение при наименовании итераторов в циклах.
                                                                                              17. Слова get/set должны быть использованы везде, где осуществляется прямой доступ к атрибуту.

                                                                                              Довольно спорный момент, get/set это так называемые свойства, которые в других языках реализованы обычным именем свойства без get/set, да и без get/set облегчается рефакторинг.
                                                                                              18. Слово compute может быть использовано в методах, вычисляющих что-либо.

                                                                                              Зачем?
                                                                                              Дайте читающему сразу понять, что это времязатратная операция.

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

                                                                                              Аналогично с предыдущем высказыванием.
                                                                                              Следует избегать сокращения init.

                                                                                              Почему? Чем оно вредно?
                                                                                              21. Переменным, представляющим GUI, следует давать суффикс, соответствующий имени типа компонента.

                                                                                              Спорно
                                                                                              23. Префикс n следует использовать для представления числа объектов.

                                                                                              Нет смысла, и не очевидно.
                                                                                              28. Следует избегать сокращений в именах.

                                                                                              Почему? Многие сокращения являются общепризнанными, например Avg, Min, Max, Sin, Cos, Cmd, Init и т.д. зачем перегружать названия и делать их длинными когда в некоторых случаях это не несёт смысла.
                                                                                              37. Содержимое файлов не должно превышать 80 колонок.

                                                                                              Спорно, экраны уже давно не такие маленькие.
                                                                                              38. Нельзя использовать специальные символы (например, TAB) и разрывы страниц.

                                                                                              Глупость! отступы следует делать только табами.
                                                                                              Такие символы вызывают ряд проблем, связанных с редакторами, эмуляторами терминалов и отладчиками, используемыми в программах для совместной разработки и кроссплатформенных средах.

                                                                                              ни разу ещё не встречался с подобными проблемами.
                                                                                              40. Заголовочные файлы должны содержать защиту от вложенного включения.

                                                                                              все компиляторы уже давно поддерживают #pragma once зачем прикручивать гуарды непонятно.
                                                                                              51. Символ указателя или ссылки в языке C++ следует ставить сразу после имени типа, а не с именем переменной.

                                                                                              Наоборот, т.к. символ указателя или ссылки в языке C++ относится к переменной а не типу.
                                                                                              53. Следует избегать неявного сравнения булевых (логических) переменных и указателей с нулём.

                                                                                              Глупость, в стандарте С++ и С нет особого типа bool который необходим для условий, соответственно не имеет смысла писать

                                                                                              if (nLines != 0) // в С++ это равносильно if (nLines != false)

                                                                                              58. Следует избегать использования break и continue в циклах.
                                                                                              Такие выражения следует использовать только тогда, когда они повышают читаемость.

                                                                                              мда… а в логике работы программ эти выражения не участвуют да?
                                                                                              Если это не указано явно, C++ считает, что возвращаемое значение имеет тип int.

                                                                                              Неужели может попробуешь скомпилировать код с функцией без типа возврата? Такое допускается, только с main и то выдаёт ошибку.
                                                                                              71. Основной отступ следует делать в два пробела.

                                                                                              Табами и только табами, т.к. табы можно настроить в большинстве IDE а пробелы нет.
                                                                                              72. Блоки кода следует оформлять так, как показано в примере 1 (рекомендуется) или в примере 2, но ни в коем случае не так, как показано в примере 3. Оформление функций и классов должно следовать примеру 2.

                                                                                              О да, 1 и 2 являются одним из вечных источников холиваров K&R или Олман в области стиля. И гайдлайн который вроде бы должен как раз такие моменты разруливать включает оба этих момента, круто!
                                                                                              89. Используйте выравнивание везде, где это улучшает читаемость.

                                                                                              ты же говорил что так

                                                                                              if (a == lowValue) compueSomething(); else if (a == mediumValue) computeSomethingElse(); else if (a == highValue) computeSomethingElseYet();

                                                                                              нельзя делать, плохо когда в гайдлайне существуют расхождения.

                                                                                              В общем очень, очень плохая статья. Многое писал КЭП, много вредных советов, много спорных моментов, много бессмысленных советов, много не освещённых моментов (например макросы, шаблоны, вложенные классы, датахолдеры, употребление explicit, перегрузка операторов, абстрактные классы(они же интерфейсы) ). Очень многое можно выкинуть и упростить.
                                                                                              • 0
                                                                                                про SomeObj.Method() и someObj.method(); категорически не согласен. Как тогда отличить SomeClassConstructor() от SomeStaticMethod(); и потом, допустим, должно быть SomeClass::Method(), но при этом someClass.method(); же, что еще больше путаницы создает.
                                                                                                • 0
                                                                                                  Ничего не понял, в чём проблема? Отличить конструктор от метода? помоему это очевидно и с именем никак не пересекается.
                                                                                                  и потом, допустим, должно быть SomeClass::Method(), но при этом someClass.method(); же, что еще больше путаницы создает.

                                                                                                  в чём конкретно? что трудно различить SomeClass::Method() и SomeClassObj.Method()? А :: и. не помогут? К тому же я вообще ни разу не встречал ситуации когда статический метод и обычный были с одинаковым именем.
                                                                                                  • 0
                                                                                                    Можно различать их по цвету, современные IDE умеют подсвечивать разным цветом различные элементы (например статические методы — оранжевые, нестатические — лазурные).