7.10 – Инструкции break и continue
Инструкция break
Хотя вы уже видели инструкцию break в контексте операторов switch (7.4 – Основы работы с оператором switch), она заслуживает более полного рассмотрения, поскольку ее можно использовать и с другими типами инструкций управления порядком выполнения программы. Инструкция break вызывает завершение цикла while, цикла do-while, цикла for или оператора switch, при этом выполнение продолжается со следующей инструкции после цикла или switch, из которых выполнен выход.
Прекращение выполнения switch
В контексте оператора switch инструкция break обычно используется в конце каждой метки case, чтобы показать, что метка case завершена (что предотвращает проваливание к последующим меткам):
#include <iostream>
void printMath(int x, int y, char ch)
{
switch (ch)
{
case '+':
std::cout << x << " + " << y << " = " << x + y << '\n';
break; // не проваливаться к следующей метке
case '-':
std::cout << x << " - " << y << " = " << x - y << '\n';
break; // не проваливаться к следующей метке
case '*':
std::cout << x << " * " << y << " = " << x * y << '\n';
break; // не проваливаться к следующей метке
case '/':
std::cout << x << " / " << y << " = " << x / y << '\n';
break;
}
}
int main()
{
printMath(2, 3, '+');
return 0;
}
Для получения дополнительной информации о проваливании вместе с некоторыми дополнительными примерами смотрите урок «7.5 – Проваливание и область видимости в switch».
Прекращение выполнения цикла
В контексте цикла инструкция break может использоваться для преждевременного завершения цикла. Выполнение продолжается со следующей инструкции после конца цикла.
Например:
#include <iostream>
int main()
{
int sum{ 0 };
// Разрешить пользователю ввести до 10 цифр
for (int count{ 0 }; count < 10; ++count)
{
std::cout << "Enter a number to add, or 0 to exit: ";
int num{};
std::cin >> num;
// выход из цикла, если пользователь вводит 0
if (num == 0)
break; // exit the loop now
// иначе добавляем число к нашей сумме
sum += num;
}
// после break выполнение продолжится здесь
std::cout << "The sum of all the numbers you entered is: " << sum << "\n";
return 0;
}
Эта программа позволяет пользователю ввести до 10 чисел и в конце отображает сумму всех введенных чисел. Если пользователь вводит 0, break приводит к преждевременному завершению цикла (до того, как будут введены 10 чисел).
Вот пример выполнения показанной выше программы:
Enter a number to add, or 0 to exit: 5
Enter a number to add, or 0 to exit: 2
Enter a number to add, or 0 to exit: 1
Enter a number to add, or 0 to exit: 0
The sum of all the numbers you entered is: 8
break также является распространенным способом выхода из преднамеренного бесконечного цикла:
#include <iostream>
int main()
{
while (true) // бесконечный цикл
{
std::cout << "Enter 0 to exit or anything else to continue: ";
int num{};
std::cin >> num;
// выход из цикла, если пользователь вводит 0
if (num == 0)
break;
}
std::cout << "We're out!\n";
return 0;
}
Пример выполнения показанной выше программы:
Enter 0 to exit or anything else to continue: 5
Enter 0 to exit or anything else to continue: 3
Enter 0 to exit or anything else to continue: 0
We're out!
break против return
Начинающим программистам иногда трудно понять разницу между break и return. Инструкция break завершает выполнение switch или цикла, и выполнение продолжается с первой инструкции после switch или цикла. Инструкция return завершает выполнение всей функции, в которой находится цикл, и выполнение продолжается в точке, где эта функция была вызвана.
#include <iostream>
int breakOrReturn()
{
while (true) // бесконечный цикл
{
std::cout << "Enter 'b' to break or 'r' to return: ";
char ch;
std::cin >> ch;
if (ch == 'b')
break; // выполнение продолжится с первой инструкции за пределами цикла
if (ch == 'r')
return 1; // return заставит функцию немедленно вернуться в вызывающую
// функцию (в данном случае main())
}
// прерывание цикла приводит к возобновлению выполнения здесь
std::cout << "We broke out of the loop\n";
return 0;
}
int main()
{
int returnValue{ breakOrReturn() };
std::cout << "Function breakOrReturn returned " << returnValue << '\n';
return 0;
}
Вот результаты двух запусков этой программы:
Enter 'b' to break or 'r' to return: r
Function breakOrReturn returned 1
Enter 'b' to break or 'r' to return: b
We broke out of the loop
Function breakOrReturn returned 0
Инструкция continue
Инструкция continue предоставляет удобный способ завершить текущую итерацию цикла без завершения всего цикла.
Вот пример использования continue:
#include <iostream>
int main()
{
for (int count{ 0 }; count < 10; ++count)
{
// если число делится на 4, пропустить эту итерацию
if ((count % 4) == 0)
continue; // перейти к следующей итерации
// если число не делится на 4, продолжаем
std::cout << count << '\n';
// инструкция continue переходит сюда
}
return 0;
}
Эта программа печатает все числа от 0 до 9, которые не делятся на 4:
1
2
3
5
6
7
9
Инструкции continue работают, заставляя текущую точку выполнения переходить в конец текущего цикла.
В случае цикла for конечное выражение цикла for по-прежнему выполняется после continue (поскольку это происходит после конца тела цикла).
Будьте осторожны при использовании инструкции continue с циклами while или do-while. Эти циклы обычно изменяют значения переменных, используемых в условии, внутри тела цикла. Если использование инструкции continue приводит к пропуску этих строк, цикл может стать бесконечным!
Рассмотрим следующую программу:
#include <iostream>
int main()
{
int count{ 0 };
while (count < 10)
{
if (count == 5)
continue; // переход к концу тела цикла
std::cout << count << ' ';
++count; // эта инструкция никогда не выполняется после того,
// как счетчик достигнет значения 5
// инструкция continue переходит сюда
}
return 0;
}
Эта программа предназначена для печати всех чисел от 0 до 9, кроме 5. Но на самом деле она печатает:
0 1 2 3 4
а затем переходит в бесконечный цикл. Когда count равно 5, оператор if принимает значение true, а continue приводит к переходу выполнения в конец цикла. Переменная count не увеличивается. Следовательно, на следующей итерации счетчик по-прежнему равен 5, условие оператора if так же равно true, и программа продолжает цикл бесконечно.
Конечно, вы уже знаете, что если у вас есть очевидная переменная счетчика, вам следует использовать цикл for, а не циклы while или do-while.
Споры по поводу использования break и continue
Многие учебники предостерегают читателей от использования break и continue в циклах как потому, что они заставляют путь выполнения прыгать, так и потому, что они могут затруднить отслеживание логики пути выполнения. Например, break в середине сложной логической части может быть пропущен, или может быть неочевидно, при каких условиях он должен сработать.
Однако при разумном использовании break и continue могут помочь сделать циклы более читаемыми, уменьшив количество вложенных блоков и уменьшив потребность в сложной логике циклов.
Например, рассмотрим следующую программу:
#include <iostream>
int main()
{
int count{ 0 }; // подсчитывает, сколько раз цикл повторяется
bool keepLooping { true }; // управляет, завершить цикл, или нет
while (keepLooping)
{
std::cout << "Enter 'e' to exit this loop or any other character to continue: ";
char ch{};
std::cin >> ch;
if (ch == 'e')
{
keepLooping = false;
}
else
{
++count;
std::cout << "We've iterated " << count << " times\n";
}
}
return 0;
}
Эта программа использует логическую переменную, чтобы контролировать, продолжать цикл или нет, а также вложенный блок, который запускается только в том случае, если пользователь не выходит.
А вот версия, которую легче понять, и которая использует инструкцию break:
#include <iostream>
int main()
{
int count{ 0 }; // подсчитывает, сколько раз цикл повторяется
while (true) // цикл до завершения пользователем
{
std::cout << "Enter 'e' to exit this loop or any other character to continue: ";
char ch{};
std::cin >> ch;
if (ch == 'e')
break;
++count;
std::cout << "We've iterated " << count << " times\n";
}
return 0;
}
В этой версии, использующей одну инструкцию break, мы избежали использования логической переменной (и необходимости понимать ее предполагаемое использование, и где изменяется ее значение), оператора else и вложенного блока.
Минимизация количества используемых переменных и уменьшение количества вложенных блоков улучшают понятность кода больше, чем break или continue навредят ему. По этой причине мы считаем приемлемым разумное использование break и continue.
Лучшая практика
Используйте break и continue, когда они упрощают логику цикла.
Споры по поводу использования ранних возвратов
В отношении инструкций return можно привести аналогичный аргумент. Инструкция return, которая не является последней инструкцией в функции, называется ранним возвратом. Многие программисты считают, что ранних возвратов следует избегать. Функция, которая имеет только одну инструкцию return в нижней части функции, отличается простотой – вы можете предположить, что функция без отклонений будет принимать свои аргументы, выполнять любую реализованную логику и возвращать результат. Наличие дополнительных инструкций return усложняет логику.
Контраргумент заключается в том, что использование ранних возвратов позволяет вашей функции завершиться, как только она будет выполнена, что уменьшает необходимость чтения ненужной логики и сводит к минимуму потребность во вложенных условных блоках, что делает ваш код более читабельным.
Некоторые разработчики выбирают золотую середину и используют ранние возвраты только в верхней части функции для проверки параметров (перехвата неверных аргументов), а затем, после этого только одну инструкцию return.
Наша позиция заключается в том, что ранние возвраты скорее полезны, чем вредны, но мы признаем, что в этой практике есть немного искусства.
Лучшая практика
Используйте ранние возвраты, если они упрощают логику вашей функции.
