7.10 – Инструкции break и continue

Добавлено 29 мая 2021 в 18:06

Инструкция 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.

Наша позиция заключается в том, что ранние возвраты скорее полезны, чем вредны, но мы признаем, что в этой практике есть немного искусства.

Лучшая практика


Используйте ранние возвраты, если они упрощают логику вашей функции.

Теги

breakC++ / CppcontinueLearnCppДля начинающихОбучениеПрограммированиеРанний возвратЦикл

На сайте работает сервис комментирования DISQUS, который позволяет вам оставлять комментарии на множестве сайтов, имея лишь один аккаунт на Disqus.com.

В случае комментирования в качестве гостя (без регистрации на disqus.com) для публикации комментария требуется время на премодерацию.