Наиболее ключевые элементы в легком [закрытом] стандарте кодирования C++

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

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

Так, для подчеркивания сущности здесь:

Какие элементы стандарта кодирования C++ самые решающие должны поддержать?

  • Ответ/правила голосования

    • 1 кандидат на ответ, предпочтительно с краткой мотивацией.

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

    • Провалите кандидатов, фокусирующихся о том, как комментировать/документировать код. Это - больший предмет, который мог бы даже заслужить его собственного сообщения.

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

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

31
задан 5 revs, 2 users 100% 8 August 2012 в 01:51
поделиться

37 ответов

Предпочтите RAII.

автоматический STL (и совместно использованный в повышении & C++ 0x), указатели могут помочь.

68
ответ дан 3 revs, 2 users 92% 27 November 2019 в 21:19
поделиться

Фигурные скобки потребовали, если у Вас есть больше чем один шаг добавления отступа:

if (bla) {
  for (int i = 0; i < n; ++i)
    foo();
}

Это помогает сохранить добавление отступа в соответствии с тем, как компилятор видит код.

1
ответ дан Frederik Slijkerman 27 November 2019 в 21:19
поделиться

Вероятно, легкая задача, но тем не менее важное правило:

Избегают неопределенного поведения.

существует очень многое из него в C++, и, вероятно, невозможно записать нетривиальное приложение, которое не зависит от него так или иначе, но общее правило должно все еще быть "неопределенным поведением, плохо". (Поскольку печально существуют программисты на C++ там, кто чувствует, что "это работает над моей машиной/компилятором", достаточно хорошо).

, Если необходимо полагаться на него, проясните всем что, почему, где и как.

2
ответ дан jalf 27 November 2019 в 21:19
поделиться

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

См. также Контрольный список для записи копии constuctor и оператора присваивания в C++

2
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

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

Примеры 'трудно для распознавания':

  • Никакие фигурные скобки, если только одна строка в условном теле
  • Использование K& R заключают в фигурные скобки размещение для пространств имен, но помещенную фигурную скобку под условиями в функциональном коде определения
  • ...
2
ответ дан xtofl 27 November 2019 в 21:19
поделиться

Остерегайтесь C API

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

, например:

// char * d, * s ;
strcpy(d, s) ; // BAD

// std::string d, s ;
d = s ;        // GOOD

Никогда strtok

strtok использования не повторно используем. Что означает, что, если один strtok запускается, в то время как другой не заканчивается, каждый повредит "внутренние данные" другого.

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

Используя API C означает использовать необработанные типы, которые могут привести к интересным ошибкам как переполнение буфера (и потенциальное повреждение стека), когда sprintf заходит слишком далеко (или строковая обрезка при использовании snprintf, который является своего рода повреждением данных). Работая над необработанными данными, malloc может легко злоупотребить, как показано следующий код:

int * i = (int *) malloc(25) ; // Now, I BELIEVE I have an array of 25 ints!
int * j = new int[25] ;        // Now, I KNOW I have an array of 25 ints!

И т.д. и т.д.

Что касается strtok: C и C++ являются поддерживающими стек языками, которые включают пользователю для не заботы о том, что функции выше его собственного на стеке, и какие функции будут вызваны ниже его собственного на стеке. strtok удаляет эту свободу "не заботы"

4
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

Общедоступное наследование должно смоделировать принцип замены Лисков (LSP).

Повторное использование кода / импорт без substituability должно быть реализовано с частным наследованием, когда очень сильная связь имеет смысл, или с агрегированием иначе.

4
ответ дан Luc Hermitte 27 November 2019 в 21:19
поделиться

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

Обновление: здесь чужое объяснение для "Встроенного C++", который, кажется, исключает исключения. Это делает следующие моменты:

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

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

4
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

Имена методов и имена переменной в общей схеме именования для непротиворечивости; я не склонен быть беспокойством очень чем-либо еще при чтении источника.

4
ответ дан Jasper Bekkers 27 November 2019 в 21:19
поделиться

Всегда, всегда, всегда делайте надлежащую инициализацию элемента данных на объектной конструкции.

я столкнулся с проблемой, где конструктор Object полагался на некоторую инициализацию "по умолчанию" для своих элементов данных. Создание кода под двумя платформами (Windows/Linux) дало различные результаты и дефицитную ошибку памяти. Результат состоял в том, что элемент данных не был инициализирован в конструкторе и использовал, прежде чем он был инициализирован. На одной платформе (Linux) компилятор инициализировал его к тому, что программист думал соответствующее значение по умолчанию. В Windows значение было инициализировано к чему-то - но мусор. На использовании элемента данных все вышло из строя. Как только инициализация была зафиксирована - больше проблемы.

5
ответ дан twokats 27 November 2019 в 21:19
поделиться

Запретите t[i]=i++; f(i++,i);, и так далее поскольку нет никаких (портативных) гарантий относительно того, что выполняется сначала.

6
ответ дан Luc Hermitte 27 November 2019 в 21:19
поделиться

Принцип наименьшего количества удивления .

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

Это не только корень, причина и проверка работоспособности для всего скучного материала как форматирование и комментарий, что инструкции, но - мне, что еще более важно - ставит акцент на коде, считанном и понятом, а не просто скомпилированном.

Это также покрывает единственную разумную качественную меру по коду, с которой я когда-либо встречался - WTF's в минуту .

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

8
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

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

8
ответ дан marijne 27 November 2019 в 21:19
поделиться

Никогда не используйте структуры без надлежащих конструкторов

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

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

struct MyStruct // BAD
{
   int i ; bool j ; char * k ;
}

struct MyStruct // GOOD
{
   MyStruct() : i(0), j(true), k(NULL) : {}

   int i ; bool j ; char * k ;
}

И если они обычно инициализируются в некотором роде, обеспечьте конструктора, чтобы позволить пользователю избежать инициализации структуры C-стиля:

MyStruct oMyStruct = { 25, true, "Hello" } ; // BAD
MyStruct oMyStruct(25, true, "Hello") ;      // GOOD

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

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

void doSomething()
{
   MyStruct s = { 25, true, "Hello" } ;
  // Etc.
}

void doSomethingElse()
{
   MyStruct s = { 25, true, "Hello" } ;
  // Etc.
}

// Etc.

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

9
ответ дан 3 revs 27 November 2019 в 21:19
поделиться

Используйте инструмент линта - т.е. Линт ПК. Это поймает многие 'структурные' проблемы инструкции по кодированию. Значение вещей, которые читают в фактические ошибки, а не проблемы стиля/удобочитаемости. (Не то, чтобы удобочитаемость не важна, но это - меньше, чем фактические ошибки).

Пример, вместо того, чтобы требовать этого стиля:

if (5 == variable)

Как способ предотвратить 'непреднамеренное присвоение' ошибка, позвольте линту найти его.

9
ответ дан Steve Fallows 27 November 2019 в 21:19
поделиться

Только тривиальное использование?: оператор, т.е.

float x = (y > 3) ? 1.0f : -1.0f;

в порядке, но это не:

float x = foo(2 * ((y > 3) ? a : b) - 1);
12
ответ дан Frederik Slijkerman 27 November 2019 в 21:19
поделиться

Предпочтите стандартно-совместимый код. Предпочтите пользоваться стандартными библиотеками.

15
ответ дан Dustin Getz 27 November 2019 в 21:19
поделиться

Фигурные скобки для любого проверяют утверждение. (Благодаря собственному опыту и укрепленный путем чтения Кода Полный v2):

// bad example - what the writer wrote
if( i < 0 ) 
    printf( "%d\n", i );
    ++i; // this error is _very_ easy to overlook!  

// good example - what the writer meant
if( i < 0 ) {
    printf( "%d\n", i );
    ++i;
}
23
ответ дан xtofl 27 November 2019 в 21:19
поделиться

Преждевременная оптимизация является корнем всего зла

Запись безопасный и корректный код сначала.

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

Никогда не полагают, что Вы оптимизируете отрывки кода лучше, чем компилятор.

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

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

Обычно, "оптимизированный" (или предположительно оптимизированный) код намного менее более ясен, и будьте склонны выражаться через сырые данные, почти машина путь, вместо более ориентированного на бизнес пути. Некоторая оптимизация полагается switchs, если, и т.д., и затем будет более трудной протестировать из-за нескольких путей выполнения кода.

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

26
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

Примечание стороны: не налагайте SESE ( Однократный Единственный Выход ) (т.е. не запрещайте больше чем один return, использование break / continue/...)

В C++, это - утопия, как throw другая точка возврата. SESE имел два преимущества в C и не допускающих исключений языках:

  • детерминированный выпуск ресурсов, который теперь аккуратно обрабатывается идиомой RAII в C++,
  • функции создания, легче поддержать, который не должен быть беспокойством, поскольку функции должны быть сохраненными короткими (как определено правилом "одной функции, одна ответственность")
27
ответ дан 3 revs 27 November 2019 в 21:19
поделиться

Знайте, кто владелец лет тот память.

  • создают объекты на стеке как можно больше, (не бесполезный новый)
  • Избегают передачи права собственности, если действительно не необходимый
  • Использование RAII и интеллектуальные указатели
  • , Если передача права собственности получает мандат (без интеллектуальных указателей), то, документ ясно код (функции должны иметь ненеоднозначное имя, всегда с помощью того же шаблона имени, как "символ * allocateMyString ()" и, "освобождает deallocateMyString (символ * p)".

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

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

как можно больше, функция/объект, выделяющая память, должна быть функцией/объектом, освобождающей его.

29
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

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

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

, Если код обработки ошибок используется для обращения к аномальному условию, обработка ошибок позволит программе ответить на ошибку корректно. Если утверждение уволено за аномальное условие, корректирующее действие не должно просто обрабатывать ошибку gracefully— корректирующее действие, должен изменить исходный код программы, перекомпилировать и выпустить новую версию программного обеспечения. Хороший способ думать об утверждениях как исполняемый файл documentation—, Вы не можете полагаться на них, чтобы заставить код работать, но они могут зарегистрировать предположения более активно, чем комментарии языка программы могут [1].

  1. McConnell, Steve. Код Полный, Второй Выпуск. Microsoft Press В© 2004. Глава 8 - Безопасное программирование
31
ответ дан Dustin Getz 27 November 2019 в 21:19
поделиться

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

32
ответ дан Ferruccio 27 November 2019 в 21:19
поделиться

Используйте вектор и строку вместо массивов C-стиля и символа *

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

Использование станд.:: строка каждый раз, когда у Вас должна быть строка.

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

станд.:: вектор: пользователь вектора может всегда находить его размер, и вектор может быть изменен в случае необходимости. Это может даже быть дано (через (& (myVector[0])) нотация) к API C. Конечно, вектор уберет после себя.

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

34
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

Удостоверьтесь, что предупреждение Вашего компилятора уровня установлено достаточно высоко (/Стена предпочтительно) так, чтобы это поймало глупые ошибки как:

if (p = 0)

, когда Вы действительно имели в виду

if (p == 0)

так, чтобы Вы не должны были обращаться к еще более глупым приемам как:

if (0 == p)

, которые ухудшают удобочитаемость Вашего кода.

36
ответ дан Ferruccio 27 November 2019 в 21:19
поделиться

Используйте ссылки вместо указателей, если это возможно. Это предотвращает постоянные защитные ПУСТЫЕ проверки.

40
ответ дан Aardvark 27 November 2019 в 21:19
поделиться

Используйте броски C++ вместо бросков C

использование:

  • static_cast
  • const_cast
  • reinterpret_cast
  • dynamic_cast

, но никогда броски C-стиля.

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

, Каждый бросок ограничил полномочия. Например, если Вы захотите удалить константу (по любой причине), [то 114] не изменит тип одновременно (который мог быть ошибкой, трудной найти).

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

48
ответ дан 4 revs, 2 users 82% 27 November 2019 в 21:19
поделиться

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

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

побочный эффект А этого правила: избегайте методов с побочными эффектами.

58
ответ дан 2 revs 27 November 2019 в 21:19
поделиться

Избегайте использования сгенерированного конструктора копирования и оператора = по умолчанию.

  • Если вы хотите, чтобы ваш объект можно было скопировать.
    • Если каждый атрибут можно тривиально скопировать, четко прокомментируйте, что вы используете неявный конструктор копирования и оператор = намеренно.
    • В противном случае напишите свои собственные конструкторы, используя поле инициализации для инициализации атрибутов и следуя порядку заголовков (который является реальный порядок построения).
  • Если вы все еще не знаете ( параметр по умолчанию ) или думаете, что не хотите копировать объекты определенного класса, объявите его конструктор копирования и оператор = частными. Таким образом, компилятор сообщит вам, когда вы делаете то, чего не хотите.
    class foo
    {
       //...
    private:
       foo( const foo& );
       const foo& operator=( const foo& );
    };

Или более понятным способом , если вы используете ускорение:

    class foo : private boost::noncopyable
    {
      ...
    };
4
ответ дан 27 November 2019 в 21:19
поделиться

Удостоверьтесь, что деструкторы определяются как виртуальный :

 class GoodClass {
 public:
   GoodClass();
   virtual ~GoodClass()
 };

 class BadClass {
 public:
   BadClass();
   ~BadClass()
 };
0
ответ дан 27 November 2019 в 21:19
поделиться
Другие вопросы по тегам:

Похожие вопросы: