Введение
После операторов переходим к конструкциям, управляющим порядком выполнения кода в зависимости от условий — if/else, switch, и тернарный оператор как компактная альтернатива простому if/else для выражений (а не операторов).
Концепция
if/else выполняет один из двух (или более, через цепочку else if) блоков кода в зависимости от истинности условного выражения — единственный оператор после if без явных фигурных скобок технически допустим синтаксисом, но менее безопасен при последующем редактировании кода. switch сравнивает значение выражения с несколькими константными вариантами (case), выполняя соответствующий блок кода — важная особенность switch в C/C++ — «проваливание» (fall-through), при котором выполнение продолжается в следующий case блок, если явно не прервано оператором break. Тернарный оператор (условие ? значение_если_истина : значение_если_ложь) — единственный в C/C++ оператор с тремя операндами, дающий значение (а не выполняющий блок операторов, как if), что делает его удобным для компактного выражения условного значения внутри более крупного выражения.
Пример кода
#include <iostream>
int main()
{
int score = 75;
if (score >= 90) {
std::cout << "Отлично" << std::endl;
} else if (score >= 70) {
std::cout << "Хорошо" << std::endl;
} else if (score >= 50) {
std::cout << "Удовлетворительно" << std::endl;
} else {
std::cout << "Неудовлетворительно" << std::endl;
}
return 0;
}
// switch с явным break и демонстрацией намеренного fall-through
#include <iostream>
void describeDay(int dayNumber)
{
switch (dayNumber) {
case 6:
case 7: // ОБА варианта (6 и 7) выполняют ОДИН И ТОТ ЖЕ код — намеренное "проваливание"
std::cout << "Выходной день" << std::endl;
break;
case 1:
case 2:
case 3:
case 4:
case 5:
std::cout << "Рабочий день" << std::endl;
break;
default: // выполняется, если значение не совпало НИ с одним явным case
std::cout << "Некорректный номер дня" << std::endl;
break;
}
}
// Тернарный оператор — компактное выражение, а не оператор (используется ВНУТРИ выражения)
#include <iostream>
int main()
{
int a = 5, b = 10;
int max = (a > b) ? a : b; // компактная альтернатива четырёхстрочному if/else для получения значения
std::cout << "Максимум: " << max << std::endl;
// Вложенные тернарные операторы — формально допустимо, но снижает читаемость при чрезмерном использовании
int grade = 75;
std::string letter = (grade >= 90) ? "A" : (grade >= 70) ? "B" : (grade >= 50) ? "C" : "F";
return 0;
}
Пояснения к коду
Цепочка if/else if/else показывает типичный, идиоматичный паттерн обработки нескольких взаимоисключающих диапазонов условий — каждое условие проверяется последовательно, и выполняется только первый блок, чьё условие оказалось истинным, с финальным else как запасным вариантом для случаев, не покрытых ни одним предыдущим условием. switch с двумя последовательными case 6:/case 7: без break между ними показывает намеренное, идиоматичное использование fall-through — оба значения должны привести к одному и тому же результату, и отсутствие break между этими двумя метками case (но не после них, где break явно присутствует) выражает это намерение компактно, без необходимости дублировать одинаковый код для обоих случаев. Тернарный оператор (a > b) ? a : b показывает его типичное использование — компактное вычисление одного из двух значений непосредственно в контексте присваивания, что было бы значительно более многословным при использовании полноценного if/else.
Подводные камни
- Забытый
breakв блокеcaseswitch, где fall-through НЕ является намеренным поведением — это одна из самых частых, классических ошибок в C/C++ коде, приводящая к непреднамеренному выполнению кода последующегоcaseблока, и многие современные компиляторы выдают предупреждение для подозрительных случаев отсутствияbreak(особенно если последующий код не пуст), но это предупреждение легко проигнорировать без внимательной проверки каждого конкретного случая. - Написание
ifбез фигурных скобок для единственного оператора, с последующим, неаккуратным добавлением второго оператора в «тот же» блок без добавления скобок при последующем редактировании кода — классическая ошибка, известная как «ошибка goto fail» по имени реального, печально известного случая в коде Apple, где отсутствие скобок привело к тому, что важная проверка безопасности фактически не выполнялась, и явное использование фигурных скобок для любогоif/else/циклов независимо от количества операторов внутри — распространённая практика предотвращения именно этой категории ошибок. - Избыточное вложение тернарных операторов, создающее трудночитаемые, «нагруженные» выражения (как пример с
letterдля нескольких диапазонов оценок) — хотя такая конструкция формально корректна и компилируется, читаемость значительно страдает по сравнению с эквивалентной, более многословной, но понятной цепочкойif/else if, и тернарный оператор лучше подходит именно для простых, единичных условных выражений, а не для замены сложной, многоступенчатой условной логики. - Использование
switchдля значений, не являющихся целочисленными константами или перечислениями (например, попытка использоватьswitchдля строк в чистом C/C++ без специальных библиотек) —switchв C/C++ принципиально ограничен сравнением с константными целочисленными значениями (включаяenum, статья 523) известными на этапе компиляции, и для условной логики, основанной на сравнении строк или других, не подходящих дляswitchтипов, нужна цепочкаif/else ifс явными вызовами сравнения (например,strcmpдля C-строк или==дляstd::string).