Какова цель использования фигурных скобок (то есть {}) для однострочного цикла if или?

284

Я читаю лекции моего лектора C++, и он написал следующее:

  • Использовать отступы //OK
  • Никогда не полагайтесь на приоритет оператора - всегда используйте круглые скобки //OK
  • Всегда используйте блок {} - даже для одной строки // не ОК, почему???
  • Объект Const с левой стороны сравнения //OK
  • Используйте unsigned для переменных, которые >= 0//nice trick
  • Установить указатель на NULL после удаления - Двойная защита удаления//Неплохо

Третья техника мне не понятна: что бы я выиграл, поставив одну строку в a { ... }?

Например, возьмите этот странный код:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

и замените его на:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;

Какая польза от использования 1-й версии?

  • 253
    Читаемость и удобство обслуживания. Не сразу очевидно, к какому блоку операторов относится j ++, и что добавление кода после него не будет связано с оператором if.
  • 27
    Мне всегда говорили использовать фигурные скобки {} для этих строк по нескольким причинам. Это делает код более понятным для чтения. Кроме того, кому-то еще через шесть месяцев может понадобиться отредактировать ваш код, так что ясность важна, и с помощью фигурных скобок вероятность возникновения ошибки будет меньше. В этом нет ничего более правильного с технической точки зрения, это просто вопрос хорошей практики. Имейте в виду, что у проекта может быть тысячи и тысячи строк кода, через которые может разобраться какой-то новый парень!
Показать ещё 35 комментариев
Теги:
coding-style
curly-braces
defensive-programming

23 ответа

517
Лучший ответ

Попробуйте также изменить i, когда мы увеличиваем j:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
        i++;

О нет! Исходя из Python, это выглядит нормально, но на самом деле это не так, как это эквивалентно:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;

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

Еще одна очень хорошая причина указана в ответе ta.speot.is.

Третий, о котором я могу думать, является вложенным if:

if (cond1)
   if (cond2) 
      doSomething();

Теперь предположим, что теперь вы хотите doSomethingElse(), когда cond1 не выполняется (новая функция). Итак:

if (cond1)
   if (cond2) 
      doSomething();
else
   doSomethingElse();

что, очевидно, неверно, так как else связывается с внутренним if.


Изменить: Поскольку это получает некоторое внимание, я уточню свое мнение. Вопрос, на который я отвечал:

Какая польза от использования 1-й версии?

Что я описал. Есть некоторые преимущества. Но правила ИМО "всегда" не всегда применяются. Поэтому я не полностью поддерживаю

Всегда используйте блок {} - даже для одной строки//не в порядке, почему???

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

  • 0
    Приятно, но всего два очка. 1) еще хуже иметь j ++; i ++; на той же линии, думая, что это нормально, его нет. 2) Хорошая IDE должна сказать вам, что я неопознан, потому что это вне области.
  • 5
    @Science_Fiction: Верно, но если вы добавите i++ до j++ , тогда обе переменные будут по-прежнему находиться в области видимости при использовании.
Показать ещё 22 комментария
324

Очень легко случайно изменить поток управления с комментариями, если вы не используете { и }. Например:

if (condition)
  do_something();
else
  do_something_else();

must_always_do_this();

Если вы прокомментируете do_something_else() одним комментарием, вы получите следующее:

if (condition)
  do_something();
else
  //do_something_else();

must_always_do_this();

Он компилируется, но must_always_do_this() не всегда вызывается.

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

  • 3
    Ооо мальчик !! это определенное поведение, которое must_always_do_this(); выполнится, если вы прокомментируете // do_something_else ();
  • 0
    Хороший ответ, но кажется, что первое предложение неправильно, поэтому я исправил это. Просто говорю на случай, если я ошибаюсь, потому что странно, что никто не заметил и не изменил это.
Показать ещё 5 комментариев
60

У меня есть сомнения относительно компетенции лектора. Учитывая его указывает:

  • OK
  • Кто-нибудь действительно напишет (или хочет прочитать) (b*b) - ((4*a)*c)? Некоторые приоритеты очевидны (или должны быть), а дополнительные скобки просто добавьте в замешательство. (С другой стороны, вы должны использовать скобки в менее очевидных случаях, даже если вы знаете, что они не необходимо).
  • Сортировка. Существует два широко распространенных соглашения для форматирования условные и циклы:
    if ( cond ) {
        code;
    }
    
    а также:
    if ( cond )
    {
        code;
    }
    
    Во-первых, я согласен с ним. Открытие { не так заметно, поэтому лучше всего предположить, что он всегда там. Во втором, однако, я (и большинство людей, с которыми я работал) не имеют проблем с отсутствием скобки для одного утверждения. (При условии, конечно, что отступы систематичны и вы последовательно используете этот стиль. (И много очень хороших программистов, пишущих очень читаемый код, опускать скобки даже при форматировании первого пути.)
  • НЕТ. Такие вещи, как if ( NULL == ptr ), достаточно уродливы, чтобы помешать читаемость. Пишите сравнения интуитивно. (Что во многих случаях приводит к константе справа.) Его 4 - плохой совет; что-нибудь что делает код неестественным, делает его менее читаемым.
  • НЕТ. Все, кроме int зарезервировано для особых случаев. к опытные программисты на C и С++, использование бит unsigned сигналов операторы. С++ не имеет реального кардинального типа (или любого другого эффективный тип поддиапазона); unsigned не работает для числовых значений, из-за правил продвижения. Числовые значения, на которых нет арифметические операции имели бы смысл, как и серийные номера, могли бы предположительно, unsigned. Я бы возражал против этого, потому что это отправляет неправильное сообщение: побитовые операции также не имеют смысла. Основное правило состоит в том, что интегральные типы int, _unless_ есть существенная причина использования другого типа.
  • НЕТ. Выполнение этого систематически вводит в заблуждение и на самом деле защищать от чего-либо. В строгом коде OO delete this; часто наиболее частый случай (и вы не можете установить this в NULL), и в противном случае большинство delete находятся в деструкторах, поэтому вы не можете получить доступ к указатель позже в любом случае. И установка его на NULL ничего не делает о любых других указателях, плавающих вокруг. Установка указателя систематически до NULL дает ложное чувство безопасности и не действительно купите вам что угодно.

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

Я предлагаю вам найти другого лектора. Тот, кто действительно знает, что он говорит.

  • 13
    Число 4 может быть уродливым, однако в этом есть цель. Он пытается предотвратить, если (ptr = NULL). Я не думаю, что когда-либо использовал delete this , это чаще, чем я видел? Я не склонен думать, что установка указателя на NULL после использования - плохая вещь, кроме YMMV. Может быть, это только я, но большинство его рекомендаций не кажутся такими уж плохими.
  • 16
    @Firedragon: большинство компиляторов будут предупреждать о if (ptr = NULL) если вы не напишите его как if ((ptr = NULL)) . Должен согласиться с Джеймсом Канзе, что уродство наличия NULL сначала делает для меня это НЕТ.
Показать ещё 22 комментария
46

Все остальные ответы защищают правило ваших лекторов 3.

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

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

Теперь посмотрим на другую сторону медали: есть ли недостаток в том, чтобы всегда использовать фигурные скобки? Другие ответы просто игнорируют этот момент. Но есть недостаток: он занимает много вертикального пространства экрана, а это, в свою очередь, может сделать ваш код нечитаемым, потому что это означает, что вам нужно прокручивать больше, чем необходимо.

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

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
    {
        throw null_ptr_error("a");
    }
    if (b == nullptr)
    {
        throw null_ptr_error("b");
    }
    if (a == b)
    {
        throw logic_error("Cannot do method on identical objects");
    }
    if (not a->precondition_met())
    {
        throw logic_error("Precondition for a not met");
    }

    a->do_something_with(b);
}

Это ужасный код, и я категорически утверждаю, что следующее более читаемо:

void some_method(obj* a, obj* b)
{
    if (a == nullptr)
        throw null_ptr_error("a");
    if (b == nullptr)
        throw null_ptr_error("b");
    if (a == b)
        throw logic_error("Cannot do method on identical objects");
    if (not a->precondition_met())
        throw logic_error("Precondition for a not met");

    a->do_something_with(b);
}

Аналогично, короткие вложенные петли выигрывают от опускания фигурных скобок:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
        for (auto j = 0; j < a.h(); ++j)
            c(i, j) = a(i, j) + b(i, j);

    return c;
}

Сравните с:

matrix operator +(matrix const& a, matrix const& b) {
    matrix c(a.w(), a.h());

    for (auto i = 0; i < a.w(); ++i)
    {
        for (auto j = 0; j < a.h(); ++j)
        {
            c(i, j) = a(i, j) + b(i, j);
        }
    }

    return c;
}

Первый код является кратким; второй код раздувается.

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

Вкратце: не записывайте ненужный код, который занимает пространство экрана.

  • 0
    +1 за разумный конкурирующий аргумент. Однако я склонен не соглашаться. Возможно, это странно, но я считаю, что ваш матричный пример гораздо сложнее читать без дополнительных скобок. С первого взгляда я могу выглядеть лучше, учитывая, что бросок выбил бы вас из функции, но для большинства примеров мне нужны скобки. Я также видел, где первая строка функции является условной и сопровождается возвратом, и это тоже не так уж плохо. Но его использование против ошибок, я думаю, слишком велико.
  • 25
    Если вы не верите в написание кода, который занимает ненужное место на экране, то вам не стоит ставить открывающую скобку на отдельной строке. Теперь мне, вероятно, придется пригнуться и убежать от святой мести GNU, но серьезно - либо вы хотите, чтобы ваш код был вертикально компактным, либо нет. И если вы делаете, не делайте вещи, предназначенные исключительно для того, чтобы сделать ваш код менее вертикально компактным. Но , как вы говорите, зафиксировав , что вы по- прежнему также необходимо удалить лишние скобки. Или, возможно, просто напишите if (a == nullptr) { throw null_ptr_error("a"); } одной строкой.
Показать ещё 16 комментариев
40

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

Наиболее частым проблемным примером, с которым я столкнулся, является следующее:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
    this_looks_like_a_then-statement_but_isn't;

Итак, когда я прихожу и хочу добавить then-statement, я могу легко закончить с этим, если я не буду осторожен:

if ( really incredibly stupidly massively long statement that exceeds the width of the editor) do_foo;
{
    this_looks_like_a_then-statement_but_isn't;
    i_want_this_to_be_a_then-statement_but_it's_not;
}

Учитывая, что для добавления фигурных скобок требуется ~ 1 секунда, и вы можете сэкономить как минимум несколько пустых минут для отладки, почему бы вам не пойти с вариантом уменьшенной двусмысленности? Для меня это кажется ложным.

  • 16
    Разве проблема не в этом примере в неправильном отступе и слишком длинных строках, а не в скобках?
  • 7
    Да, но следование рекомендациям по дизайну / кодированию, которые являются только «безопасными», если предположить, что люди также следуют другим рекомендациям (таким как отсутствие слишком длинных линий), кажется, вызывает проблемы. Если бы брекеты были с самого начала, в этой ситуации было бы невозможно получить неправильный блок if.
Показать ещё 4 комментария
19

My 2c:

Использование отступов

Очевидно,

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

Я бы не использовал слова "никогда" и "всегда", но в целом я вижу, что это правило полезно. На некоторых языках (Lisp, Smalltalk) это не проблема.

Всегда используйте блок {} - даже для отдельной строки

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

Объект Const с левой стороны сравнения

Условия йоды? Нет пожалуйста. Это больно читаемости. Просто используйте максимальный уровень предупреждения при компиляции кода.

Используйте unsigned для переменных, которые >= 0

OK. Забавно, я слышал, что Страуструп не согласен.

Установить указатель на NULL после удаления - Защита двойного удаления

Плохой совет! Никогда не указывайте указатель на удаленный или несуществующий объект.

  • 5
    +1 только за последний пункт в одиночку. Необработанный указатель в любом случае не имеет бизнеса, владеющего памятью.
  • 2
    Что касается использования unsigned: не только Страуструп, но и K & R (в С), Херб Саттер и (я думаю) Скотт Мейерс. На самом деле, я никогда не слышал, чтобы кто-то, кто действительно понимал правила C ++, доказывал необходимость использования unsigned.
Показать ещё 8 комментариев
16

он более интуитивно понятен и легко понятен. Это делает цель понятной.

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

  • 0
    Makes the intent clear +1, это, вероятно, самая краткая и точная причина.
13

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

setCompIds( const std::string& compId, const std::string& compSubId );

тогда как для замены необходимы два вызова:

setCompId( const std::string& compId );
setCompSubId( const std::string& compSubId );

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

if ( condition )
   setCompIds( compId, compSubId );

Для этого:

if ( condition )
   setCompId( compId );
setCompSubId( compSubId );

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

Я заметил, что astyle теперь имеет опцию --add-brackets, которая позволяет добавлять скобки, где их нет, и я настоятельно рекомендую это, если вы когда-нибудь окажетесь в том же положении, что и я.

  • 5
    Однажды я увидел документацию, в которой была замечательная чеканка "Microsoftligent". Да, с глобальным поиском и заменой можно совершать существенные ошибки. Это просто означает, что глобальный поиск и замена должны использоваться разумно, а не по микрослайджентски.
  • 4
    Я знаю, что это не моя посмертная работа, но если вы собираетесь выполнять замену текста в исходном коде, то вы должны делать это в соответствии с теми же правилами, которые вы использовали бы для хорошо зарекомендовавшей себя замены текста. на языке: макросы. Вы не должны писать макрос #define FOO() func1(); \ func2(); (с переводом строки после обратной косой черты), то же самое касается поиска и замены. Тем не менее, я видел «всегда использовать фигурные скобки», продвинутые в качестве правила стиля именно потому, что это избавляет вас от переноса всех ваших макросов с несколькими операторами в do .. while(0) . Но я не согласен.
Показать ещё 6 комментариев
8

Самый подходящий пример, который я могу придумать:

if(someCondition)
   if(someOtherCondition)
      DoSomething();
else
   DoSomethingElse();

С какими if будет спарен else? Отступ подразумевает, что внешний if получает else, но это не так, как его увидит компилятор; внутренний if получит else, а внешний if не будет. Вы должны были бы знать, что (или посмотрите, как это работает в режиме отладки), чтобы выяснить, почему этот код может не оправдать ваши ожидания. Это становится более запутанным, если вы знаете Python; в этом случае вы знаете, что отступ определяет блоки кода, поэтому вы ожидаете, что он будет оцениваться в соответствии с отступом. С#, однако, не дает летящего флип о пробелах.

Теперь, я сказал, я не очень согласен с этим правилом "всегда использовать скобки" на его лице. Это делает код очень вертикально шумным, уменьшая способность быстро его считывать. Если утверждение:

if(someCondition)
   DoSomething();

... тогда это должно быть написано именно так. Утверждение "всегда использовать скобки" звучит как "всегда окружает математические операции с круглыми скобками". Это превратило бы очень простое утверждение a * b + c / d в ((a * b) + (c / d)), введя возможность пропустить близкую пару (проклятие многих кодеров) и для чего? Порядок операций хорошо известен и хорошо соблюдается, поэтому круглые скобки являются излишними. Вы должны использовать только круглые скобки для выполнения другого порядка операций, чем обычно применяется: a * (b+c) / d например. Блочные скобки сходны; используйте их, чтобы определить, что вы хотите делать, в случаях, когда он отличается от значения по умолчанию и не является "очевидным" (субъективным, но обычно довольно обычным).

  • 3
    @AlexBrown ... это была моя точка зрения. Правило, изложенное в OP, гласит: «всегда используйте скобки, даже для отдельных строк», с чем я не согласен по той причине, о которой я говорил. Скобки могли бы помочь с самым первым примером кода, потому что код не будет вести себя так, как с отступом; вам придется использовать скобки, чтобы соединить else с первым, if вместо второго. Пожалуйста, удалите понижающий голос.
8

Я использую {} всюду, за исключением нескольких случаев, когда это очевидно. Одна строка - один из следующих случаев:

if(condition) return; // OK

if(condition) // 
   return;    // and this is not a one-liner 

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

Другой пример в С# с использованием statment

using (D d = new D())  // OK
using (C c = new C(d))
{
    c.UseLimitedResource();
}

что эквивалентно

using (D d = new D())
{
    using (C c = new C(d))
    {
        c.UseLimitedResource();
    }
}
  • 1
    Просто используйте запятые в выражении using и вам не нужно :)
  • 1
    @minitech Это просто не работает - вы можете использовать запятую только тогда, когда типы равны, но не для неравных типов. Способ Лукаса сделать это - канонический способ, IDE даже форматирует это по-другому (обратите внимание на отсутствие автоматического отступа при втором using ).
5

Просматривая ответы, никто в явном виде не указал, какую практику я делаю, рассказывая историю вашего кода:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

становится:

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0) j++;
}

Поместите j++ в ту же строку, что и если кто-то должен сигнализировать кому-либо: "Я хочу, чтобы этот блок никогда не увеличивал j". Из coursethis стоит только, если линия настолько упрощена, насколько это возможно, потому что установка точки останова здесь, как говорится в peri, не будет очень полезной.

На самом деле, я просто столкнулся с частью Twitter Storm API, у которой есть этот "сортировка" кода в java, вот фрагмент relvant из кода выполнения, на стр. 43 этого слайд-шоу:

...
Integer Count = counts.get(word);
if (Count=null) count=0;
count++
...

В блоке for loop есть две вещи, поэтому я бы не ввел этот код. I.e никогда:

int j = 0;
for (int i = 0 ; i < 100 ; ++i) if (i % 2 == 0) j++;

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

Если вы действительно хотите телеграфировать кому-то еще, однолинейная работа использует только тернарный оператор или ?: form:

for (int i = 0 ; i < 100 ; ++i) (i%2 ? 0 : >0) j++;

Но это сводится к коду-гольфу, и я думаю, что не очень хорошая практика (мне не ясно, должен ли я поставить j ++ с одной стороны : или нет). NB Я не запускал тройной оператор на С++ раньше, я не знаю, работает ли это, но он существует.

Короче:

Представьте, как ваш читатель (т.е. человек, поддерживающий код) интерпретирует вашу историю (код). Сделайте максимально понятным для них. Если вы знаете, что новичок-кодер/ученик поддерживает это, возможно, даже оставить как можно больше {}, просто чтобы они не запутались.

  • 1
    (1) Помещение выражения в одну строку делает его менее читабельным, а не более. Особенно просто мыслить как прирост легко упускается из виду. Есть ли поставить их на новую строку. (2) Конечно, вы можете поместить цикл for в одну строку, почему это не должно работать? Это работает по той же причине, по которой вы можете опустить скобки; Новая строка просто не имеет значения в C ++. (3) Ваш пример условного оператора, кроме ужасного, является недействительным C ++.
  • 0
    @KonradRudolph спасибо, я немного заржавел на C ++. Я никогда не говорил (1) был более удобным для чтения, но это будет означать быть , что этот кусок кода должен был быть онлайн одна строка. (2) Мой комментарий заключался в том, что я не смог бы прочитать его и знать, что это сработало, будь то вообще или как предполагалось; это пример того, чего нельзя делать по этой причине. (3) Спасибо, я давно не писал C ++. Я исправлю это сейчас.
Показать ещё 1 комментарий
4

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

int error = 0;
enum hash_type hash = SHA256;
struct hash_value *hash_result = hash_allocate();

if ((err = prepare_hash(hash, &hash_result))) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &client_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &server_random)) != 0)
    goto fail;
if ((err = hash_update(&hash_result, &exchange_params)) != 0)
    goto fail;
    goto fail;
if ((err = hash_finish(hash)) != 0)
    goto fail;

error = do_important_stuff_with(hash);

fail:
hash_free(hash);
return error;

Это выглядит хорошо. Проблема с этим очень легко пропустить, особенно когда функция, содержащая код, намного больше. Проблема в том, что goto fail выполняется безоговорочно. Вы легко можете себе представить, как это расстраивает (заставляя спросить, почему последний hash_update всегда терпит неудачу, ведь все хорошо выглядит в функции hash_update).

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

if (something) goto fail;

Но не следующий.

if (something)
    goto fail;
  • 0
    Именно так. Только не ставьте (совершенно ненужный) символ новой строки + отступ, и вы полностью обойдете эту проблему, которую все всегда так быстро поднимают.
4

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

if (condition) doSomething();
else doSomethingElse();

if (condition) doSomething();
    doSomething2(); // Looks pretty obviously wrong
else // doSomethingElse(); also looks pretty obviously wrong
  • 5
    Второй вариант приведет к ошибке компиляции, потому что else не связан с if .
  • 0
    Одна не очень заметная проблема со встроенными функциями заключается в том, что большинство IDE по умолчанию изменяют его на стиль с отступом при использовании утилиты автоформатирования.
Показать ещё 1 комментарий
4

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

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}

выглядит захламленным для меня. Он разделяет цикл for и оператор if на отдельные действия, когда на самом деле ваше намерение является единственным действием: считать все целые числа, делящиеся на 2. На более выразительном языке это может быть написано примерно так:

j = [1..100].filter(_%2 == 0).Count

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

int j = 0;
for (int i = 0 ; i < 100 ; ++i)
  if (i % 2 == 0)
{
    j++;
}
  • 7
    Мне нравится, как всем удается игнорировать for (int i = 0; i < 100; i += 2); ради продолжения спора об отступах ;-) Возможно, у нас может быть целый отдельный булфайт, как «лучше» выразить логику «для каждого i в определенном диапазоне с определенным свойством» в C ++ без цикла, используя некоторую кошмарную комбинацию стандартных алгоритмов, filter_iterator и / или counting_iterator .
  • 1
    Кроме того, если бы у нас было это, то мы могли бы не согласиться с тем, как сделать отступ в массивном единственном утверждении.
Показать ещё 2 комментария
4

wrt 6: Это безопаснее, потому что удаление нулевого указателя является no-op. Поэтому, если вы случайно случайно пройдете этот путь дважды, вы не будете вызывать повреждение памяти, освобождая память, которая является бесплатной или была выделена для чего-то еще.

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

В большинстве случаев вы можете избежать необходимости этого, используя auto_ptrs

  • 6
    Если вам случится пройти этот путь дважды, вы получите ошибку программирования. Установка указателя на ноль, чтобы сделать эту ошибку менее вредной, не решает основную проблему.
  • 1
    Согласен, но я видел это рекомендованным ранее, и я верю, что это в некоторых профессиональных стандартах программирования. Я больше комментировал, почему профессор плаката придумал это, а не когда это было хорошо
Показать ещё 1 комментарий
4

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

3

Другой пример добавления фигурных скобок. Как только я искал ошибку и нашел такой код:

void SomeSimpleEventHandler()
{
    SomeStatementAtTheBeginningNumber1;
    if (conditionX) SomeRegularStatement;
    SomeStatementAtTheBeginningNumber2;
    SomeStatementAtTheBeginningNumber3;
    if (!SomeConditionIsMet()) return;
    OtherwiseSomeAdditionalStatement1;
    OtherwiseSomeAdditionalStatement2;
    OtherwiseSomeAdditionalStatement3;
}

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

{
    ...
    OtherwiseSomeAdditionalStatement3;
    SetAnotherVariableUnconditionnaly;
}

В результате SetAnotherVariableUnconditionnaly выполняется, когда SomeConditionIsMet(), но быстрый парень этого не заметил, потому что все строки почти схожи по размеру, и даже когда условие возврата имеет вертикальный отступ, это не так заметно.

Если условный возврат форматируется следующим образом:

if (!SomeConditionIsMet())
{
    return;
}

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

  • 0
    Если ваш быстрый кодер не может быть обеспокоен тем, чтобы определить синтаксический оператор return в теле функции перед добавлением чего-либо к нему, вы не должны позволять быстрому кодеру приближаться к вашему коду. Вы не остановите такого парня от троллинга вокруг вашего кода, включая фигурные скобки.
  • 0
    @cmaster Он больше не работает с нами. В любом случае, подсветка синтаксиса хороша, но помните, что есть люди, которые не видят ясно (я видел даже сообщение от слепого программиста в прошлом году).
3

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

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

  • 2
    В любом случае, кроме открытия { на своей линии.
2

Я считаю, что первое должно быть четким, а затем вторым. Это дает ощущение закрытия инструкций, при этом мало кода отлично, когда код становится сложным {...} помогает много, даже если это endif или begin...end

//first
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
{
    if (i % 2 == 0)
    {
        j++;
    }
}


//second
int j = 0;
for (int i = 0 ; i < 100 ; ++i)
    if (i % 2 == 0)
        j++;
i++;
1

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

if (condition)
  statement;

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

if (condition)
{
  statement;
  statement;
}

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

if (condition) {
  statement;
  statement;
}

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

Оператор строки if с одним оператором на следующей строке также может быть проблематичным, если кодовая база существенно использует форму

if (condition) statement;

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

if (x1 > xmax) x1 = xmax;
if (x1 < xmin) x1 = xmin;
if (x2 > xmax) x2 = xmax;
if (x2 < xmin) x2 = xmin;
etc.

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

1

Всегда иметь фигурные скобки - очень простое и надежное правило. Однако код может выглядеть inelegant, когда есть много брекетов. Если правила позволяют опустить фигурные скобки, тогда должны быть более подробные правила стиля и более сложные инструменты. В противном случае это может легко привести к хаотическому и запутанному (не изящному) коду. Поэтому поиск отдельного правила стиля отдельно от остальных руководств по стилю и используемых инструментов, вероятно, бесплоден. Я просто приведу некоторые важные сведения об этом правиле № 3, о которых даже не упоминалось в других ответах.

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

// pedantic rule #3
if ( command == Eat )
{
    eat();
}
else
{
    if ( command == Sleep )
    {
        sleep();
    }
    else
    {
        if ( command == Drink )
        {
            drink();
        }
        else
        {
            complain_about_unknown_command();
        }
    }
}

Вместо этого, если они видят это, они могут даже предложить написать это следующим образом:

// not fully conforming to rule #3
if ( command == Eat )
{
    eat();
}
else if ( command == Sleep )
{
    sleep();
}
else if ( command == Drink )
{
    drink();
}
else
{
   complain_about_unknown_command();
}

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

Вторая деталь (которая также часто забывается сторонниками этого правила) заключается в том, что ошибки, которые могут произойти, никогда не только из-за нарушений этого правила № 3. На самом деле это почти всегда связано с нарушениями правила № 1 (с которым никто не спорит). Опять же, с точки зрения автоматических инструментов, нетрудно сделать инструмент, который немедленно жалуется, когда нарушается правило № 1, и поэтому большинство ошибок могут быть обнаружены своевременно.

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

1

Я должен признать, что не всегда использовать {} для одной строки, но это хорошая практика.

  • Допустим, вы пишете код без скобок, который выглядит так:

    for (int я = 0; я < 100; ++ i) for (int j = 0; j < 100; ++ j) DoSingleStuff();

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

  • Операция памяти быстрее. Допустим, у вас большой объем и создайте большие массивы внутри (без new, чтобы они были в стеке). Эти массивы удаляются из памяти сразу после того, как вы покидаете область видимости. Но возможно, что вы используете этот массив в одном месте, и он будет в стеке на некоторое время и будет каким-то мусором. Поскольку стек имеет ограниченный и довольно малый размер, можно увеличить размер стека. Поэтому в некоторых случаях лучше написать {}, чтобы предотвратить это. ПРИМЕЧАНИЕ это не для одной строки, а для таких ситуаций:

    if (...) {   // Некоторые вещи...   {//у нас нет if, while и т.д.       //SomeOtherStuff   }   //SomeMoreStuff }

  • Третий способ использования аналогичен второму. Это просто не сделать очиститель стека, а открыть некоторые функции. Если вы используете mutex в длинных функциях, обычно лучше блокировать и разблокировать непосредственно перед доступом к данным и сразу после окончания чтения/записи. ПРИМЕЧАНИЕ, этот способ используется, если у вас есть свои собственные class или struct с constructor и destructor для блокировки памяти.

  • Что еще:

    if (...)  если (...)     Некоторые вещи(); еще  SomeOtherStuff();//переходит ко второму, если, но аллигмент показывает, что он первый...

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

ВАЖНОЕ ИЗМЕНЕНИЕ. Если вы пишете компиляцию скобок кода для одной строки, ничего не делает, но если ваш код будет интерпретироваться, он очень медленно замедляет код. Очень немного.

1

Лучше всего установить указатель на NULL, когда вы закончите с ним.

Вот пример, почему:

Класс A выполняет следующие действия:

  • Выделяет блок памяти
  • Затем через некоторое время он удалит этот блок памяти, но не устанавливает указатель в NULL

Класс B выполняет следующие действия

  • Выделяет память (и в этом случае ему присваивается тот же блок памяти, который был удален классом А.)

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

Рассмотрим следующую задачу:

Что делать, если в классе A возникла логическая ошибка, которая привела к записи в память, которая теперь принадлежит классу B?

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

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

Если указатель удаленной памяти был установлен в NULL, вы бы получили ошибку исключения, как только любые логические ошибки в классе A попытались написать указателю NULL.

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

Кроме того: если вы собираетесь голосовать, пожалуйста, объясните.

  • 2
    Если произошла логическая ошибка, ее следует исправить, а не маскировать.
  • 0
    @ Barmar, OP говорит ... 6. Установите Pointer на NULL после удаления - Двойное удаление защиты // неплохо. Некоторые люди ответили, что не установили его в Null, и я говорю, почему он должен быть установлен в NULL, какая часть из 6. мой ответ на установку NULL не вписывается в 6?
Показать ещё 1 комментарий

Ещё вопросы

Сообщество Overcoder
Наверх
Меню