Pull to refresh

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

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

Предлагаю 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 Спасибо всем прокомментировавшим! Обсуждение (как я и ожидал) получилось интересней статьи :-) Я не вношу замечания в статью по двум причинам. Многие замечания (равносправедливые) противоречат друг другу (что не удивительно, при обсуждении подобных тем). И второе, если я внесу все правки, то обсуждение станет совершенно непонятным и статья утратит свою лучшую часть :-) Пусть уж всё остаётся как есть и «ошибки»(?) допущенные в статье станут иллюстрацией к обсуждению.
Tags: программированиеifelse
Hubs: C++
Total votes 88: ↑61 and ↓27 +34
Comments 113
Comments Comments 113

Popular right now