Всё ли вы знаете про if?

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

    Предлагаю 5К мыслей и ссылки на style guide-ы (далее SG). Надеюсь, что эта статья станет приятным и лёгким чтивом. И я уверен, что она затрагивает далеко не все вопросы и надеюсь на содержательное обсуждение.


    Строго говоря, подобные вещи можно написать не только про if. Именно if выбран почти случайно (в конце концов, а почему бы и нет?). Идею этой статьи подкинул f0b0s. Её (идею) поддержали сразу несколько человек, я тоже начал ждать статью; но так и не дождавшись, решил сам поднять это знамя. f0b0s, спасибо за идею, извиняюсь, если опередил, жду комментариев.

    Для полноты уделим секунду банальным вещам.

    Фигурные скобки


    Идеальный вариант писать фигурные скобки везде, а каждое выражение if/else/else_if

    // ХОРОШО
    if (a == b) {
      foo();
    } else {
      bar();
    }

    причём некоторые SGы рекомендуют писать else на новой строке

    // Не
    } else {
    // а
    }
    else {
    // или
    }
    else
    {

    Тогда else получается с таким же отступом, что и соответствующий if. Но в этом вопросе нет единства.

    Если вы всё же опускаете скобки (в чём нет никакого криминала), то надо учитывать два аспекта. Во-первых, придерживаться одинаковой тактики для блоков if и else:

    // ПОХУЖЕ
    if (a == b) {
      foo();
      bar();
    } else
      bazzz();

    И, во-вторых, не записывать весь if в одну строку

    // СОВСЕМ ПЛОХО
    if (cond()) act();
    else err();

    Смешение стилей в пределах одного if/else затрудняет его чтение, а размещение нескольких выражений в одной строке ещё и затрудняет отладку; в последнем примере cond() и act() расположены в одной строке; это затрудняет поиск ошибок по номером срок и отладку.

    И ещё два замечания о блоках


    Создавайте переменные только при необходимости


    Используйте преимущества С++. Если переменная нужна только в блоке, то и создавайте её только в блоке.

    // ПЛОХО
    SomeClass p; // нужен только в блоке
    if (x == y) {
      p.moveTo(x, y);
      p.logResultsOfMovement();
    }

    // ХОРОШО
    if (x == y) {
      SomeClass p; // нужен только в блоке
      p.moveTo(x, y);
      p.logResultsOfMovement();
    }

    Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)

    Описывайте сперва нормальный ход событий


    В выражениях if/else сперва описывайте нормальный ход событий, а в блок else выносите обработку нештатных ситуаций. Это упрощает чтение программы:

    // ХОРОШО
    bool errorFlag = openConnection();
    if (!errorFlag) {
      doJob();
    } else {
      dropJob();
    }

    Логические выражения


    C++ не С


    Прежде всего, в C++ не стоит следовать стилю C писать

    // ПЛОХО
    if (float_num) {}
    if (int_num) {}

    Стандарт С++ не гарантирует, что float(0) и даже int(0) реализуются как «все биты 0» (хотя, конечно, это работает, но это чисто случайно :-)).

    Если вам нужно проверить на равенство 0, то лучше написать именно это:

    // ХОРОШО
    if (float_num != .0) {}
    if (int_num != 0) {}

    В качестве условия следует использовать только логические величины. Это же, кстати относится и к циклам:

    // ХОРОШО
    while (true) {}
    // ПЛОХО
    while (1) {}
    for (;;) {}

    Разделяйте логические выражения на части


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

    // ПЛОХО
    if (figureX > leftLimit && figureX < fightLimit && figureColor == GREEN)

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

    // ХОРОШО
    if (figureX > leftLimit &&
        figureX < fightLimit &&
        figureColor == GREEN)

    Но совсем идеально, ввести в программу дополнительные переменные

    // ОЧЕНЬ МИЛО
    bool isInside = figureX > leftLimit && figureX < fightLimit;
    bool isGreen = figureColor == GREEN;
    if (isInside && isGreen)

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

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

    Выражения в условиях


    Выражения лучше выносить из условий

    // ХОРОШО
    bool dataReaded = readDdata(fileName);
    if (dataReaded) {}

    Это делает код на много читабельный, чем

    // ПЛОХО
    if (readDdata(fileName)) {}

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

    Ну и, конечно, «каждому выражению своя строка!».

    // СОВСЕМ ПЛОХО
    if (readDdata(getFileNameFromArray(getEnvAsArray()))) {}

    Опять же, сложно читать, сложно отлаживать, сложно локализовать ошибки.

    Присвоения в условиях


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

    // ПЛОХО
    if (lid = getLabel() == curentLabel())

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

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

    Если писать не

    // ПЛОХО?
    if (x == 1)

    а

    // ХОРОШО?
    if (1 == x)

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

    Кажется ничего не забыл


    Если что-то забыл, спасибо за комментарии :-)

    Пара хороших SG:

    Классический SG. Обратите внимание на ссылки внизу — там есть ещё более обширные документы.

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

    upd Спасибо всем прокомментировавшим! Обсуждение (как я и ожидал) получилось интересней статьи :-) Я не вношу замечания в статью по двум причинам. Многие замечания (равносправедливые) противоречат друг другу (что не удивительно, при обсуждении подобных тем). И второе, если я внесу все правки, то обсуждение станет совершенно непонятным и статья утратит свою лучшую часть :-) Пусть уж всё остаётся как есть и «ошибки»(?) допущенные в статье станут иллюстрацией к обсуждению.
    AdBlock has stolen the banner, but banners are not teeth — they will be back

    More
    Ads

    Comments 113

      +19
      Стандарт С++ не гарантирует, что float(0) и даже int(0) реализуются как «все биты 0»
      Тем не менее стандарт гарантирует, что при приведении float(0) или int(0) к bool мы получим true.

      Так что этот совет про
      // ПЛОХО
      if (float_num) {}
      if (int_num) {}
      я бы исключил из статьи.
        +19
        при приведении float(0) или int(0) к bool мы получим true
        false конечно же, опечатался.
          –2
          Согласен только частично :-) Моё объяснение, согласен, не точное… даже неверное… позор мне, в общем.

          Но вот сам совет хороший. Он даёт не только указанное мной преимущество.
          Во-первых, он сразу проверяет тип, чем помогает не совершать ошибки.
          Во-вторых, использовать не bool просто не концептуально. Это пережиток C, которому не место в C++. Согласитесь?
            +3
            Во-первых, он сразу проверяет тип, чем помогает не совершать ошибки.
            Если вы проверяете на равенство/неравенство нулю, то тип вам не важен, я думаю.

            Во-вторых, использовать не bool просто не концептуально.
            Условие в if и while приводится к bool, если оно не bool. Писать if (x) вместо if (0 != x) это уже идиома. Чтобы поддержать эту идиому в классах определяется operator bool и operator!

            Сравните

            boost::shared_ptr<T> p;
            if (p) { ... }
            if (NULL != p.get()) { ... }

            Первый вариант намного лаконичнее.
              +2
              Многие конструкции уже укоренились, например проверка указателя. Тут вопрос в том как это применять. Если я проверяю указатель — это одно, но если я использую if(x) для проверки знаменателя на равенство нулю — это уже совсем другое. Последний вариант, как и многие ему подобные, использовать само собой не стоит.
            +19
            Числа с плавающей точкой лучше проверять не на равенство .0, а на попадание в окрестность нуля. Потому что у вас всегда будет погрешность, кроме совсем тривиальных случаев.
              +2
              Соглашаюсь обеими руками! Не вношу в статью только потому, что она немного не об этом и без того уже слишком длинная. Пусть это замечание останется украшением обсуждения :-)
                +3
                Кстати проблема легко решается наличием всего пары строк:

                static inline bool fuzzyCompare1(float p1, float p2)
                {
                    return abs(p1 - p2) <= 1.0E-4;
                }


                или

                static inline bool fuzzyCompare2(float p1, float p2)
                {
                    return abs(p1 - p2) <= 0.00001f * min(abs(p1), abs(p2));
                }


                Первый вариант сравнивает с постоянным epsilon = 1.0E-4, второй с процентом от меньшего числа.
                  +1
                  Рекомендую хорошую статью о сравнении чисел с плавающей точкой.
                  В частности, там подробно разбираются некоторые тонкости при реализации относительного сравнения.
              +10
              Про то, что for(;;) это плохо — тоже глупость. Это легальный способ написать бесконечный цикл не получив предупреждения «conditional expression is constant».
                0
                Согласен. В плане легальности, все три способа, которые я привожу, эквивалентны. Но тут речь не о легальности, а об… эстетике.
                  +4
                  Норвежские программисты предлагают свой способ: doc.qtsoftware.com/4.5/qtglobal.html#forever
                    +1
                    зачем лишние «затычки»?
                      0
                      Конечно, в forever смысла особо нету, но есть и полезнейший foreach, например.
                  +1
                  for(;;) — самый корректный способ бесконечного цикла. уберите из плохих практик, а то плохому научите )
                    –1
                    Ну это спорный вопрос :-) вот тут совет 60
                    geosoft.no/development/cppstyle.html#Types
                    И я с ними согласен. В С++ (а речь именно про него) типы заслуживают особого уважения.
                  +1
                  Чтобы компилятор сразу же дёрнул вас за руку, некоторые люди вырабатывают привычку писать сравнения наоборот (признаюсь, я не смог её выработать в себе :-))
                  Прийдется выработать привычку ставить максимальный уровень предупреждений и трактовать их как ошибки ;)
                    0
                    Ох. Эта тема уже обсуждалась. Я принадлежу к людям, которые считают, что совершенство недостижимо :-)
                    +11
                    >Описывайте сперва нормальный ход событий

                    Довольно часто встречаются такие ситуации:

                    if(isOk)
                    {
                    //~100 строк кода
                    }
                    else
                    {
                    setError();
                    return -1;
                    }

                    Такие блоки гораздо легче читать, если сначала описать более короткую последовательность действий.
                      –2
                      Та ну, здесь абсолютно правильно автор пишет. Вы что чаще будете читать — эти 100 строк кода, или 2 строки с обработкой ошибки?
                        +10
                        Та ну, я лучше сразу увижу две строчки обработки ошибки и перейду к остальной части кода. Две строчки мне не повредят никак. А вот в случае с автором, за сотней строк можно и не заметить обработку ошибки, что плачевно.

                        Всё, что для вас «абсолютно правильно» не означает, что это автоматически правильно для других. Автор описал достаточно спорные моменты и попытался обобщить, напялить на всех, а это вредно.
                          +10
                          Мне кажется, эти 100 строчек хорошо бы вынести в отдельную функцию и дать ей понятное имя. Так всем будет проще.
                            +1
                            Да, вынесение 100 строчек кода в отдельную функцию, во многих случаях оправданное решение. Читаемость кода повысится.

                            Но дальше возникают довольно-таки холиварные моменты. Одни готовы терпеть 100 строчек кода в одной из веток if'а, другие закроют глаза на узкоспециализированную функцию, использующуюся один раз и имеющую в параметрах разнородные данные (контексты, внутренние структуры разных уровней, буферы с данными, текущее значение счетчика (если снаружи цикл)). Это уже дело вкуса и, как правило, разработчики между собой договариваются, как в подобных случаях поступать.

                            Скажем, в команде, в которой сейчас я работаю, принято в функцию выносить фрагменты кода, которые используются два раза или более. А те 100 строк кода изрядно разбавляем комментариями с описанием что и зачем делается. Вполне возможно, что кому-то из здесь присутствующих неудобно следовать этому правилу. Главное, что всегда можно выбрать фломастер, вкус которого вам понравится.
                              +1
                              Чем больше строк в функции, тем сложнее её тестировать. А если вы раза 3-4 не вынесете 100 строк кода в отдельную фукнцию, у вас получится простыня на 300-400 строк, в которой разобраться будет намного сложнее.
                          +12
                          самое приятное это когда мы разобрались с особыми случаями и забыли о них.

                          if (error_occured) {
                          return -1;
                          }
                          // no errors further
                            +6
                            кроме того такой стиль кодирования уменьшает вложенность и код выглядит проще
                            сравните

                            void f()
                            {
                            if(ok1)
                            {
                            do1();
                            if(ok2)
                            {
                            // а вот тут собственно код вашей фукнции - уже третий уровень вложенности
                            }
                            else
                            return -2;
                            }
                            else
                            return -1;
                            }

                            void f()
                            {
                            // проверим все условия
                            if(!ok1)
                            return -1;
                            do1();
                            if(!ok2)
                            return -2;
                            // теперь все гарантированно хорошо, можно сделать то что мы намеревались
                            // и никаких лишних {} и отступов
                            }

                              0
                              а если такую обработку еще и отформатировать в виде:

                              void f()
                              {
                                 // проверим все условия
                                 if(!ok1) return -1;
                                 do1();
                                 if(!ok2) return -2;
                                 // теперь все гарантированно хорошо, можно сделать то что мы намеревались
                                 // и никаких лишних {} и отступов
                              }


                              то будет еще красивее.
                              Правда это противоречит одному из правил в статье, но на мой взгляд, такие ситуации, как раз являются исключением из этого правила.
                              –7
                              Как по мне, одним из основополагающих принципов является «функция должна иметь одну точку возврата». Множественные return-ы только запутывают.
                                +3
                                Это холиварное утверждение. Многоуровневая вложенность ифов запутывает не меньше.

                                Выбрасывание исключения между прочим — тоже может быть точкой выхода из функции, так что теперь, исключения не использовать?
                                  +1
                                  описанный мной пример, а также, скажем, исключения подтверждают, что это правило — полный отстой :)
                                    0
                                    И за что заминусовали человека? Высказывание абсолютно верное, выведено такими личностями, как Кнут, Дейкстра и Вирт.
                                      +2
                                      Ага, наверное поэтому у Кнута в каждой второй функции несколько ретурнов. Кстати, когда он писал на Паскале его и гоуту в конец функции не смущал, как заменитель ретурна, в те времена Exit'а в Паскале не было.
                                        +1
                                        Чтобы не быть голословным:

                                        node *sum(p,q) /* compute the sum of two binary trees */
                                          node *p,*q;
                                        {
                                          register node *pl,*ql;
                                          register int s;
                                          easy=1;
                                          if (!p) return q;
                                          if (!q) return p;
                                          pl=left(p); ql=left(q);
                                          s=compare(pl,ql);
                                          if (s==0) {
                                            @<Add |right(p)| to |right(q)| and append this to |succ(pl)|@>@;
                                            easy=0;@+return p;
                                          } else {
                                            if (s<0) @<Swap |p| and |q| so that |p>q|@>;
                                            q=sum(right(p),q);
                                            if (easy) goto no_sweat;
                                            else {
                                              ql=left(q);
                                              s=compare(pl,ql); /* does a carry need to be propagated? */
                                              if (s==0) { /* yup */
                                                change(&p->r,right(q));
                                                recycle(ql);@+free_node(q);
                                                if (pl) succ(pl);
                                                else change(&p->l,get_avail());
                                                return p;
                                              }
                                              else easy=1; /* nope */
                                            }
                                          no_sweat: change(&p->r,q);
                                            return p;
                                          }
                                        }


                                        5 ретурнов, 1 гоуту
                                          –2
                                          А теперь быстренько объясните, что делает этот код. Мне не понятно. :) (вы выдернули его из контекста, а у мои телепатические способности не позволяют его восстановить.)

                                          ЗЫ ладно, вычеркните Кнута из списка.
                                          –1
                                          Дык видать народ не читал классики, чего уж там ;) И, кроме как, кучей ретурнов уже не умеют красиво организовать потом исполнения.
                                            +1
                                            Я читал не только классиков 60-х 70-х, но и современных классиков. Так вот Фаулер в «Рефакторинге» утверждает, что правило единственного выхода из функции нужно забыть как ошибку прошлого.

                                            Мир не стоит на месте, поэтому, читайте современные книги.
                                              –1
                                              Один советует одно, другой рекомендует другое. Для вас одно — для меня другое. Захламляйте код кучей точек выхода и сопровождающие попомнят вас не злым тихим словом.
                                                +1
                                                Мне кажется, что код, с несколькими точками выхода, который имеет 2 уровня вложенности читается и сапровождается намного проще, чем код с одной точкой выхода и четырьмя уровнями вложенности.

                                                Не надо слепо верить словам Дейкстры (это правило придумал именно он), хотя, на вкус, как известно, все фломастеры… ну вы знаете.
                                                0
                                                Дык правило «один вход — один выход» возникло не просто так.

                                                Ключевое слово тут «структурность». Больше структуры — легче рассуждать о поведении кода.

                                                На этом же основываются и design patterns. Например, увидев в коде синглтон, программист сразу же начинает представлять себе, лол, что можно с ним делать, а что — нельзя.
                                      +5
                                      если в else return, то логичнее

                                      if(!isOk)
                                      {
                                      setError();
                                      return -1;
                                      }

                                      //~100 строк кода
                                        0
                                        Да, я, например, люблю писать небольшие функции, и при возникновении ошибок завершать функцию с return или throw (в зависимости от характера ошибки).
                                        0
                                        Такие ситуации не должны встречаться. Если у Вас получилась простыня на 100 строк, то стоит разбить их на функции. Читаемость кода повысится сильнее.
                                        –10
                                        Статья как статья, а вот некоторые комментарии перед кусками кода улыбнули =)…
                                        // ПОХУЖЕ вообще убило =). Извиняюсь за оффтоп.
                                          +2
                                          В добавок к уже сказанному.
                                          Например, мне проще выносить негатив в if, а обработку в else. К примеру, кусок кода:
                                          if (some_error)
                                            throw Error();
                                          else
                                          {
                                            // make job
                                          }
                                          как по мне выглядит логичнее.
                                          Ну и создание лишних переменных тоже зло. Опять же, как по мне, так легче в одном условии всё описать, чем плодить гору лишних переменных ради одного условия. Ясное дело, если они нужны далее, тогда вопросов нет. Но в противном случае, зачем их плодить?
                                          Опять же, выражения:
                                          if (readDdata(getFileNameFromArray(getEnvAsArray()))) {}
                                          if (lid = getLabel() == curentLabel())
                                          вполне читабельны и юзабельны. Не вижу проблемы.

                                          Вам бы стоило сделать акцент на контингент. Для новичок читать исходники какого-нибудь embedded конечно сложно, но зачем адекватным людям усложнять себе же жизнь лишним мусором? :)
                                            +4
                                            Зачем после throw нужен else?
                                              0
                                              просто пример выброса ошибки, замените на ProcessError(), без разницы. Смысл в «малом кол-ве кода»
                                                0
                                                Я к тому, что в случае с throw не нужен else c фигурными скобками, увеличивающий вложенность.
                                                  0
                                                  Я к тому, что просто хотел показать в первом блоке «малое кол-во кода» и первое, что пришло в голову — это throw. Я согласен, что пример говняный, но суть была не в самом throw, а показать, что, имхо, сразу нужна обработка ошибок, а потом уже всё остальное. Да, ошибся, но в прошлом коменте вроде пояснил, что к чему.
                                                  Так, надеюсь, понятно? ;)
                                                    +5
                                                    Ага :)

                                                    Невозможность править комментарии — отстой :(
                                              +2
                                              Например, мне проще выносить негатив в if, а обработку в else.

                                              Наверное это как наполовину полный / пустой стакан. Интересно психологи думали об этом?
                                              +1
                                              int a = 0;
                                              if( a ) что-то-там

                                              Насколько я знаю, с expression внутри if происхдит неприятность — он evaluates в true или false. И, опять же, насколько я помню, char, short, int и __int64 если ==0 то evaluates в false, в противном случае в true.

                                              float a = 0.0
                                              if( a ) что-то-там

                                              Так делать нельзя не потому что «все биты 0», а потому что float по стандарту не гарантирует что то, что мы туда положили будет 100% соответствовать тому, что изъяли. Из-за внутреннего представления: 1.0 / 2.0 может получиться 0.49999999999 :). Это тоже написано в стандарте.

                                              В соатльном согласен, спасибо за статью.
                                              BTW, еще говорят что:

                                              if( что-то-длинное
                                                  && что-то длинное
                                                  && что-то длинное )
                                              


                                              читатеся лучше, чем

                                              if( что-то-длинное &&
                                                  что-то-длинное &&
                                                  что-то-длинное )
                                              


                                              Но лично я не уверен.
                                                +1
                                                Самый первый комментарий, процитирую:
                                                «Тем не менее стандарт гарантирует, что при приведении float(0) или int(0) к bool мы получим false. „
                                                  0
                                                  Да, я читал. Здесь уточнил, в каком именно месте и как он это гарантирует :). BTW, float( 0.0 ) только в том случае если мы только что положили 0.0 и не делали резких движений. Если 0 там подрозумевается как результат математической операции — то может не срастись :).
                                                0
                                                [offtopic]вот здесь бы понадобилась идея о плюсовании части комментария[/offtopic]
                                                согласен со второй частью про «что-то-длинное»
                                                0
                                                !errorFlag наверное имелось ввиду errorFlag
                                                  0
                                                  Нет, тут всё правильно. Имелось ввиду «если ошибки не(!) произошло, то делаем что-то, в противном случае — бросаем работу».
                                                    +1
                                                    переименуйте в haveError — будет яснее
                                                  0
                                                  еще бы стоило посоветовать почитать Code Complete глава 31 (а лучше всю книгу)
                                                    –5
                                                    // СОВСЕМ ПЛОХО
                                                    if (cond()) act();
                                                    else err();


                                                    я обычно в таких случаях пишу
                                                    if (cond()) act();
                                                           else err();
                                                      0
                                                      Разницы с приведенным кодом почти нет. Плохость кода в примере не только в том, что он плохо читается как есть, а в основном в том, что его неудобно расширять. Добавляя код прийдется не забыть правильно расставить фигурные скобки и перенести на новую строку act(); или err(); — больше работы и диффы сложнее смотреть.
                                                        +4
                                                        cond()? act(): err();
                                                          –1
                                                          коротко и однозначно
                                                            +2
                                                            особенно это удобно отлаживать, ага.
                                                              +4
                                                              Задача проследить путь выполнения программы в данном месте не представляет никакой трудности ни в одном из известных мне дебагеров. К тому же такая запись обычна применима в том случае, когда условие действительно простое, и если надо прервать выполнение если это условие ложно то можно использовать conditional breakpoints. Ах да, вариант переписать это в if else когда это действительно надо для отладки никто не отменял.
                                                              Да, я предпочту переписать через месяц пару таких простых кусков кода с добавлением подробного логирования, чем получить кашу из if else с кучей уровней вложенности.
                                                          0
                                                          Я считаю, что фигурные скобки надо ставить всегда.

                                                          А запись
                                                          else
                                                          bazzz();
                                                          ещё хуже, чем
                                                          else bazzz();

                                                          Как-то долго втыкал в кусок кода примерно следующего вида (линия между строк важна):
                                                          if(somethin)
                                                          a();
                                                          b();

                                                          c();

                                                          Долго не мог понять, почему выполняется b(), а a() при этом не отрабатывает.
                                                          +1
                                                          По моему
                                                          if (expr)
                                                          {
                                                          //do something
                                                          }
                                                          else
                                                          {
                                                          //do another work
                                                          }
                                                          логичнее и смотриться лучше. Зачем скобки ставить на уровень if-else, это книжная запись чтобы экономить место. У нас же места навалом.
                                                            0
                                                            То же самое хотел написать =) Только еще отступов добавить внутри блоков.
                                                            Когда много вложенных фигурных скобок их очень легко цеплять взглядом, если открывающая и закрывающая на одной вертикали.
                                                              +1
                                                              Скобки ставятся на уровне if и else, чтобы подчеркнуть, что последующий код неразрывно связан с данным if или else (java-style). Если ставить скобки на новой строке, то if и код после него выглядят как независимые блоки(C#-style). То же касается объявления методов/функций.
                                                                0
                                                                «Независимые» вы можете отодвинуть пустой строкой, или парой таких строк. Да и блог все-таки про c++.
                                                                «Неразрывно связанный» код для меня читается хуже.
                                                                  0
                                                                  каким же образом язык c#, появившийся много позже, чем c++ у нас уже заимел свою нотацию устаканненую? всегда пишу скобки на следущей строке, при чем тут c#, если я его открывал для ознакомления только?
                                                                  +2
                                                                  Терпеть не могу такое написание. И не понимаю почему оно логичнее и не могу сказать, что оно смотрится лучше. И экран у меня ограничен по высоте, а хочется видеть побольше кода на экране:(
                                                                    +1
                                                                    Ну, привычки и code convention у людей разные. Главное чтобы стиль был одинаковый по всему проекту. Насчёт побольше кода, imho функции, которые занимают более 1-1.5 экранов являются первыми кандидатами на простенький рефакторинг extract method.
                                                                  +4
                                                                  Небольшая идея по улучшению кода, не относящаяся к статье.
                                                                  При определении, находится ли число в некотором интервале, начало интервала, число и конец интервала лучше записывать в порядке их расположения на числовой оси.
                                                                  Было:
                                                                  bool isInside = figureX > leftLimit && figureX < rightLimit;
                                                                  Стало:
                                                                  bool isInside = leftLimit < figureX && figureX < rightLimit;
                                                                    0
                                                                    Жалко, что в Си нельзя написать как в Питоне:
                                                                    isInside = leftLimit < figureX < rightLimit
                                                                      +1
                                                                      Написать то можно. Только в результате получится не то, что вы имели в виду :)
                                                                    0
                                                                    Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)

                                                                    Это вас кто-то обманул. Локальные переменные живут в стеке. Завести такой объект — одна инструкция: добавить размер объекта к esp.
                                                                      +4
                                                                      Ну, если создание объекта занимает много времени, то сбережет в тех случаях, когда мы не попадаем в блок. Ну и вообще это хорошая практика — объявлять переменные непосредственно перед их использованием.
                                                                        0
                                                                        Ну и вообще это хорошая практика — объявлять переменные непосредственно перед их использованием.

                                                                        Это тонкий философский вопрос. :)
                                                                        0
                                                                        Но широко распространенные компиляторы, типа gcc или msvc выделяют кадр стека один раз при входе в функцию и не меняют esp/ebp ходя по блокам внутри неё.

                                                                        Кроме того, вызов конструктора и деструктора будет весить наверно больше, чем изменение размера кадра стека.
                                                                        0
                                                                        Нет ли здесь противоречия:
                                                                        // ПЛОХО
                                                                        SomeClass p; // нужен только в блоке
                                                                        if (x == y) {
                                                                          p.moveTo(x, y);
                                                                          p.logResultsOfMovement();
                                                                        }

                                                                        и
                                                                        // ОЧЕНЬ МИЛО
                                                                        bool isInside = figureX > leftLimit && figureX < fightLimit;
                                                                        bool isGreen = figureColor == GREEN;
                                                                        if (isInside && isGreen) {
                                                                        }

                                                                        в том смысле, что переменные isInside && isGreen нужны только для проверки условия…
                                                                        Хотя согласен, при отладке breakpoint + evaluate на if
                                                                        if(someCheck())
                                                                        {
                                                                        
                                                                        }

                                                                        выполнит someCheck, а на
                                                                        bool isGreen = someCheck();
                                                                        if(isGreen)
                                                                        {
                                                                        }
                                                                        нет, что может быть важно, если someCheck возвращает другой результат при повторном вызове.

                                                                        А скажите, вот так форматировать код:
                                                                        if (a == b)
                                                                          {
                                                                          foo();
                                                                          }
                                                                        else
                                                                          {
                                                                          bar();
                                                                          }
                                                                        неприлично? Никак не могу привыкнуть писать «по стандарту», т.е. скобку сразу после if…
                                                                          0
                                                                          Вы попробуйте «по стандарту» делать — когда будете разбираться в программе — это упростит понимание. Главное — в одну кашу не пишите.

                                                                          Всё дело в том, что
                                                                            +1
                                                                            if (a == b){
                                                                            foo();
                                                                            }else{
                                                                            bar();
                                                                            }

                                                                            1) выглядит компактнее
                                                                            2) конец if-else ловится хорошо
                                                                              +1
                                                                              а вот как раз на мой вкус именно в моём варианте скобочки хорошо ловятся (на одном уровне, отдельная строка, блок отчетливо виден). А компактность должна заботить только заказчика, если он оплачивает код построчно ;-)
                                                                              Тем более почти все приведенные примеры «ХОРОШО» не добавляют компактности…
                                                                          +2
                                                                          >>// ПЛОХО?
                                                                          >>if (x == 1)
                                                                          >>а
                                                                          >>// ХОРОШО?
                                                                          >>if (1 == x)

                                                                          Нет, не хорошо. Есть ведь ещё перегрузка операторов и неявное преобразование из типа. Писать надо так как наиболее понятно. Думать о том, что может быть ошибка и из-за этого делать уродский код не стоит. В противном случае весь код окажется уродским и непонятным.
                                                                            +2
                                                                            В чем проблема с оператором совсем непонятно. Неужели сложно сделать
                                                                            bool operator ==(const A& a, const B& b)
                                                                            {
                                                                            	// Implementation
                                                                            }
                                                                            
                                                                            bool operator==(const B& b, const A& a)
                                                                            {
                                                                            	return a == b;
                                                                            }
                                                                              0
                                                                              Эти примеры не о том. Они созданы для использования операторов вне классов. К тому же представим, что типов преобразования не два A и B, а больше. С каждым вот таким использованием количество методов будет расти в геометрической прогрессии. Между прочим твои примеры как раз и показывают как делать не надо, тема называется «конструкторы и преобразования». Вот условно назову некий оператор #

                                                                              1.operator#(x); // ошибка

                                                                              И моё высказывание относилось вовсе не к тому, как её преодолеть. А именно к тому, что не нужно писать уродский код полагаясь на особенности некой реализации. Реализация в конце концов может оказаться совсем другой.
                                                                                0
                                                                                Если вы сравниваете экземпляр класса A с экземпляром класса B, в большинстве случаев у вас будет определен либо конструктор A::A(const B&) либо A::operator B(). В первом случае вам нужен bool operator==(const A&, const A&), во втором bool operator==(const B&, const B&), которые и так будут реализованы.
                                                                            +3
                                                                            На мой взгляд не важно как именно расставлены скобки и {}. Важно чтобы стиль был один и тот же во всем проекте. И все разработчики придерживались этого стиля.
                                                                              +3
                                                                              Почитайте Макконела «Совершенный код». Там все это рассматривается лучше, подробнее, понятнее и интереснее.
                                                                                –1
                                                                                Извините, придираюсь :)
                                                                                bool dataReaded = readDdata(fileName);

                                                                                не readed, а read…
                                                                                глагол неправильный…

                                                                                зы: сам недавно узнал что не payed, a paid :D

                                                                                Статья каррошая, я очень трогательно отношусь к хорошему визуальному восприятию кода
                                                                                  +1
                                                                                  // ПЛОХО
                                                                                  SomeClass p; // нужен только в блоке
                                                                                  if (x == y) {
                                                                                  p.moveTo(x, y);
                                                                                  p.logResultsOfMovement();
                                                                                  }

                                                                                  // ХОРОШО
                                                                                  if (x == y) {
                                                                                  SomeClass p; // нужен только в блоке
                                                                                  p.moveTo(x, y);
                                                                                  p.logResultsOfMovement();
                                                                                  }

                                                                                  Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)

                                                                                  Ну не правда. Возьмите этот код. Отключите оптимизацию, откомпелируйте… потом возьмем дизасемблер… и посмотрим что получилось. Так вот. Даже без оптимизации, компилятор создает одинаковый код. :)
                                                                                  (я беру компилятор от MS)
                                                                                  Если же включить функцию оптимизации, то код красиво так сложится в разы… и там вы уже даже с трудом обнаружите иницилизацию класса.

                                                                                  Остальные примеры исследовать с помощью написанием тестовых прог и их исследованием не стал. :)

                                                                                  P.S.
                                                                                  ДРугое дело, что код получается более правильный, но вот с точки зрения компилятора, без разницы.
                                                                                    0
                                                                                    Я уже писал выше. Если конструктор SomeClass тяжёлый, а ситуация, когда x == y случается редко, в производительности вы выиграете заметно.
                                                                                      0
                                                                                      тесты есть? :)
                                                                                        0
                                                                                        Код тут

                                                                                        Paul@paulcomp /cygdrive/d/src/cpp/benchmarks
                                                                                        $ g++-4 --version
                                                                                        g++-4 (GCC) 4.3.2 20080827 (alpha-testing) 1
                                                                                        Copyright © 2008 Free Software Foundation, Inc.
                                                                                        This is free software; see the source for copying conditions.  There is NO
                                                                                        warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
                                                                                        
                                                                                        
                                                                                        Paul@paulcomp /cygdrive/d/src/cpp/benchmarks
                                                                                        $ g++-4 -O2 -o test test.cpp
                                                                                        
                                                                                        Paul@paulcomp /cygdrive/d/src/cpp/benchmarks
                                                                                        $ time ./test.exe 1
                                                                                        
                                                                                        real    0m6.816s
                                                                                        user    0m6.625s
                                                                                        sys     0m0.015s
                                                                                        
                                                                                        Paul@paulcomp /cygdrive/d/src/cpp/benchmarks
                                                                                        $ time ./test.exe
                                                                                        
                                                                                        real    0m0.733s
                                                                                        user    0m0.656s
                                                                                        sys     0m0.015s
                                                                                          0
                                                                                          В примере условие 0 == i % 10 эмулирует ситуацию, когда x == y случается в 9 раз реже, чем x != y.
                                                                                            0
                                                                                            ясно про что вы… мы говорим о мягком с теплым.
                                                                                            сделайте конструктор пустым… тогда у вас и ситуация в корне изменится.
                                                                                              +2
                                                                                              Здравствуйте. Я же писал про тяжёлый конструктор, а вы говорите «сделайте пустым».
                                                                                    +1
                                                                                    Всё зависит от контекста, иногда «плохие» приёмы очень выразительны. Ничего нет хуже для понимания, чем 20 строк «правильно» оформленного пятистрочного алгоритма. Тут главное без фанатизма.
                                                                                      0
                                                                                      интересная заморочка.
                                                                                        0
                                                                                        В общем, очень много спорных практик.

                                                                                        for(;;), проверки на ошибки и т.п.

                                                                                        Вообще говоря, я склонен все проверки делать как раз наоборот (precondition, assertion, postcondition — все проверяют на ошибки, а не на нормальный ход, равно как и CodeContract в C#).
                                                                                          –2
                                                                                          Вот если бы вы, уважаемый автор, хотя бы изредка читали хорошие книжки по программированию…
                                                                                            0
                                                                                            Так я готов! Посоветуйте! (Кстати, тогда от вашего комментария будет польза и мне и людям.)
                                                                                              –2
                                                                                              > Так я готов! Посоветуйте!

                                                                                              «Космонавтом быть хочу, пусть меня научат». Так получается?

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

                                                                                              Почему эти самые люди сами не могут себе помочь? Никто же не придет их спасать. Первый шаг сделать очень просто. :)

                                                                                              Вопрос о расставлении фигурных скобок очень важен, лол.

                                                                                              > Создавайте переменные только при необходимости

                                                                                              Это верно.

                                                                                              > Используйте преимущества С++. Если переменная нужна только в блоке, то и создавайте её только в блоке. Это сбережёт процессорное время, память… в общем всё, что так дорого каждому человеку, исповедующему гуманистические идеалы :-)

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

                                                                                              > В качестве условия следует использовать только логические величины. Это же, кстати относится и к циклам

                                                                                              Это следует из того, что if представляет собой именно что условие, и из соображений типобезопасности (type safety) неявного преобразования и прочей хреноты нужно избегать. Впрочем, к реальным программам на Си/Си++ это не относится, потому что языки такие. :)

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

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

                                                                                              Проще говоря,

                                                                                              > if (figureX > leftLimit && figureX < fightLimit && figureColor == GREEN)

                                                                                              То же самое, что и

                                                                                              > if ((figureX > leftLimit) && (figureX < fightLimit) && (figureColor == GREEN))

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

                                                                                              > Выражения лучше выносить из условий

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

                                                                                              > Такую программу на много проще читать, и кроме того, переменные isInside и isGreen могут пригодиться в дальнейшем.

                                                                                              Вот когда пригодятся, тогда и вынесем. :) YAGNI.

                                                                                              > По уже изложенным причинам, в логических выражениях лучше не выполнять присвоения:

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

                                                                                              > Ну и, конечно, «каждому выражению своя строка!».

                                                                                              Налицо явная путаница в терминах «выражение» (expression) и «утверждение» (statement).

                                                                                              > то вы можете не бояться опечатать и написать "=" вместо "==".

                                                                                              Ни разу не попадался на эту ошибку. Счастливчик. :)

                                                                                              Почитайте Вирта (Алгоритмы и Структуры данных), SICP, HTDP, etc. И не пишите таких дезинформирующих статей.
                                                                                                0
                                                                                                > «Космонавтом быть хочу, пусть меня научат». Так получается?

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

                                                                                                > Почему эти самые люди сами не могут себе помочь?
                                                                                                > Никто же не придет их спасать. Первый шаг сделать очень просто. :)

                                                                                                Откуда вы это взяли? «не могут», «не придет», «Первый шаг»? Вы прям экстрасенс какой-то :-) И шаги уже посчитали, и возможности всех оценили… не много на себя берёте? (извините, просто интересуюсь :-))

                                                                                                Ну а всё остальное конструктивно (почти везде). Спасибо. Спорить не буду, на что-то я уже тут отвечал, на что-то отвечали другие, что-то бесспорно…

                                                                                                Вирта читал. Не очень понимаю, как эта книга относится к теме статьи. Тут народ советовал «Совершенный код» С. Макконнелла — вот это по теме было. Статья-то не про алгоритмы и не про структуры.
                                                                                                  0
                                                                                                  > И снова тоже самое — если вы видите в этом что-то постыдное (я не вижу)

                                                                                                  Книжки искать долго что ли? Чай гуглом все умеют пользоваться.

                                                                                                  > Или вы сторонник точки зрения «уже хаскель знаю и никем быть не хочу и учиться тоже фтопку»? Я же не прошу вас меня учить, я спрашиваю про книги.

                                                                                                  Нет, я сторонник высказывания «век живи, век учись, — помрешь все равно дураком». ;)

                                                                                                  > Ну а всё остальное конструктивно (почти везде). Спасибо. Спорить не буду, на что-то я уже тут отвечал, на что-то отвечали другие, что-то бесспорно…

                                                                                                  «Почти везде» — где именно неконструктивно? Там нету ни одного наезда на Си/Си++.

                                                                                                  > Вирта читал. Не очень понимаю, как эта книга относится к теме статьи. Тут народ советовал «Совершенный код» С. Макконнелла — вот это по теме было. Статья-то не про алгоритмы и не про структуры.

                                                                                                  У Вирта хорошо написано о структурном программировании. В статье говорится о том, как улучшить структуру кода (должно говориться, вообще-то).

                                                                                                  «Совершенный код» не читал, но исправлю при первой же возможности. Главное, чтобы не оказалось как с Г.Бучем и его ООП. :)
                                                                                                    0
                                                                                                    При поиске книжек гугл человека никак не может заменить. Хороших книжек очень мало (по сравнению с вообще книжками :-)). Тут как раз интересно мнение опытного человека.

                                                                                                    Почему вы читаете «неконструктивно» == «наезд»? ,-) Я отношу к неконструктивному всё, что не относится к статье. По разным причинам. Хотя бы из-за субъективности («Ни разу не попадался на эту ошибку.»; статья-то не про вас; и даже не про меня :-))

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

                                                                                                    А совершенный код, я вам (именно вам!) очень советую! И ваше мнение мне было бы очень интересно! С одной стороны, книжка очень толковая. (Очень!) С другой стороны, в ней есть наезды на рекурсию, которые даже меня на столько покоробили, что я написал про это заметку :-) Вы, как любитель рекурсии (и я с вами солидарен полностью) наверняка испытаете сильные эмоции, читая этот труд :-) По крайней мере, Макконнелл пишет прямым текстом, что вас бы он уволил не задумываясь :-)
                                                                                                      0
                                                                                                      > Почему вы читаете «неконструктивно» == «наезд»? ,-)

                                                                                                      Стереотипы. :)

                                                                                                      > Я отношу к неконструктивному всё, что не относится к статье. По разным причинам. Хотя бы из-за субъективности («Ни разу не попадался на эту ошибку.»; статья-то не про вас; и даже не про меня :-))

                                                                                                      Да, субъективно очень. Не удержался.

                                                                                                      > А совершенный код, я вам (именно вам!) очень советую! И ваше мнение мне было бы очень интересно! С одной стороны, книжка очень толковая. (Очень!) С другой стороны, в ней есть наезды на рекурсию, которые даже меня на столько покоробили, что я написал про это заметку :-) Вы, как любитель рекурсии (и я с вами солидарен полностью) наверняка испытаете сильные эмоции, читая этот труд :-) По крайней мере, Макконнелл пишет прямым текстом, что вас бы он уволил не задумываясь :-)

                                                                                                      Ну точно надо почитать. :)
                                                                                            0
                                                                                            Спасибо, сейчас активно изучаю Java и статья пришлась впору :)
                                                                                            Прошу не минусуйте, почти все моменты с конструкторами схожи в этих языках.

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

                                                                                            Obj = <src.act>
                                                                                            if (<Serv.Uid.<Obj.Tag0.linkToAnotherObject>.tag0.someInteger> == <Obj.SomeAnotherInteger>)

                                                                                            и даже больше и страшнее. Слава богу, что появилась возможность и желание изучать нормальные логичные языки программирования :)
                                                                                              0
                                                                                              >Можно ли быстро понять, что тут написано?
                                                                                              >
                                                                                              >// ПЛОХО
                                                                                              >if (figureX > leftLimit && figureX < fightLimit && figureColor == GREEN)
                                                                                              >
                                                                                              >Подсветка, конечно, помогла бы, но и без подсветки следующее выражение читается на много проще:
                                                                                              >
                                                                                              >// ХОРОШО
                                                                                              >if (figureX > leftLimit &&
                                                                                              > figureX < fightLimit &&
                                                                                              > figureColor == GREEN)

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

                                                                                              if (isFigureTarget(figureX, figureColor))
                                                                                                0
                                                                                                Вынесение в отдельные функции — это всегда хорошо. Но тут есть два риска.
                                                                                                1. Во многих случаях функции могут неоправданно расплодиться.
                                                                                                2. А внутри функции что будет? Такое же непонятное выражение? Название, конечно прояснить его смысл, но вот чтение (поиск ошибок) не облегчится.
                                                                                                  0
                                                                                                  1. Да, могут. тут нужно чувство меры. как правило, если в выражении больше 3 бинарных логических операторов, то уже пора.
                                                                                                  2. Да, будет непонятное выражение. Вот тут уже можно его и на стадии разбить. Ну и повторно использовать, если вдруг.
                                                                                                0
                                                                                                Забавно, ссылка на ГеоСофт — когда-то и сам активно пользовался тем документом, а нашёл его в то время, когда каталоги ссылок были популярнее поисковиков.

                                                                                                Кто-нибудь знает, кто его составил? Что это за ГеоСофт такой?

                                                                                                Only users with full accounts can post comments. Log in, please.