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
.
Наша позиция заключается в том, что ранние возвраты скорее полезны, чем вредны, но мы признаем, что в этой практике есть немного искусства.
Лучшая практика
Используйте ранние возвраты, если они упрощают логику вашей функции.