8.11 — Przerwij i kontynuuj

Przerwa

Chociaż widziałeś już break stwierdzenie w kontekście switch oświadczenia (8.5 - Podstawy instrukcji Switch), zasługuje ono na pełniejsze potraktowanie, ponieważ można go również używać z innymi typami instrukcji przepływu sterowania. Instrukcja break powoduje zakończenie pętli while, pętli do-while, pętli for lub instrukcji switch, a wykonanie jest kontynuowane od następnej instrukcji po przerwaniu pętli lub przełącznika.

Przerywanie przełącznika

W kontekście instrukcji switch na końcu instrukcji zwykle używane jest break każdego przypadku, aby oznaczyć, że sprawa została zakończona (co zapobiega przejściu do kolejnych przypadków):

#include <iostream>

void printMath(int x, int y, char ch)
{
    switch (ch)
    {
    case '+':
        std::cout << x << " + " << y << " = " << x + y << '\n';
        break; // don't fall-through to next case
    case '-':
        std::cout << x << " - " << y << " = " << x - y << '\n';
        break; // don't fall-through to next case
    case '*':
        std::cout << x << " * " << y << " = " << x * y << '\n';
        break; // don't fall-through to next case
    case '/':
        std::cout << x << " / " << y << " = " << x / y << '\n';
        break;
    }
}

int main()
{
    printMath(2, 3, '+');

    return 0;
}

Zobacz lekcję 8.6 -- Przełącz awarię i zakres aby uzyskać więcej informacji na temat awarii wraz z kilkoma dodatkowymi przykładami.

Przerywanie pętli

W kontekście pętli można zastosować instrukcję break używane do wcześniejszego zakończenia pętli. Wykonanie jest kontynuowane od następnej instrukcji po zakończeniu pętli.

Na przykład:

#include <iostream>

int main()
{
    int sum{ 0 };

    // Allow the user to enter up to 10 numbers
    for (int count{ 0 }; count < 10; ++count)
    {
        std::cout << "Enter a number to add, or 0 to exit: ";
        int num{};
        std::cin >> num;

        // exit loop if user enters 0
        if (num == 0)
            break; // exit the loop now

        // otherwise add number to our sum
        sum += num;
    }

    // execution will continue here after the break
    std::cout << "The sum of all the numbers you entered is: " << sum << '\n';

    return 0;
}

Program ten pozwala użytkownikowi wpisać do 10 liczb i wyświetla sumę wszystkich wprowadzonych na końcu liczb. Jeśli użytkownik wprowadzi 0, przerwa spowoduje wcześniejsze zakończenie pętli (przed wprowadzeniem 10 liczb).

Oto przykładowe wykonanie powyższego programu:

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

A break jest również powszechnym sposobem wyjścia z zamierzonej nieskończonej pętli:

#include <iostream>

int main()
{
    while (true) // infinite loop
    {
        std::cout << "Enter 0 to exit or any other integer to continue: ";
        int num{};
        std::cin >> num;

        // exit loop if user enters 0
        if (num == 0)
            break;
    }

    std::cout << "We're out!\n";

    return 0;
}

Przykładowe uruchomienie powyższego programu:

Enter 0 to exit or any other integer to continue: 5
Enter 0 to exit or any other integer to continue: 3
Enter 0 to exit or any other integer to continue: 0
We're out!

Przerwa vs powrót

Nowi programiści czasami mają problemy ze zrozumieniem różnicy pomiędzy instrukcją break i return. A break kończy przełącznik lub pętlę, a wykonanie jest kontynuowane od pierwszej instrukcji poza przełącznikiem lub pętlą. Instrukcja return kończy całą funkcję, w której znajduje się pętla, a wykonywanie jest kontynuowane w miejscu, w którym funkcja została wywołana.

#include <iostream>

int breakOrReturn()
{
    while (true) // infinite loop
    {
        std::cout << "Enter 'b' to break or 'r' to return: ";
        char ch{};
        std::cin >> ch;

        if (ch == 'b')
            break; // execution will continue at the first statement beyond the loop

        if (ch == 'r')
            return 1; // return will cause the function to immediately return to the caller (in this case, main())
    }

    // breaking the loop causes execution to resume here

    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;
}

Oto dwa uruchomienia tego programu:

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

Klasa Instrukcjacontinue zapewnia wygodny sposób zakończenia bieżącej iteracji pętli bez przerywania całej pętla.

Oto przykład użyciacontinue:

#include <iostream>

int main()
{
    for (int count{ 0 }; count < 10; ++count)
    {
        // if the number is divisible by 4, skip this iteration
        if ((count % 4) == 0)
            continue; // go to next iteration

        // If the number is not divisible by 4, keep going
        std::cout << count << '\n';

        // The continue statement jumps to here
    }

    return 0;
}

Ten program wypisuje wszystkie liczby od 0 do 9, które nie są podzielne przez 4:

1
2
3
5
6
7
9

A continue Instrukcja działa w ten sposób, że bieżący punkt wykonania przeskakuje na sam dół bieżącej pętli.

W przypadku pętli for, instrukcja końcowa pętli for (w powyższym przykładzie przykład ++count) nadal jest wykonywany po kontynuacji (ponieważ dzieje się to po zakończeniu treści pętli).

Zachowaj ostrożność podczas używania instrukcji continue w pętlach while lub do-while. Pętle te zazwyczaj zmieniają wartość zmiennych używanych w warunku wewnątrz treści pętli. Jeśli użycie instrukcji continue spowoduje pominięcie tych linii, wówczas pętla może stać się nieskończona!

Rozważ następujący program:

#include <iostream>

int main()
{
    int count{ 0 };
    while (count < 10)
    {
        if (count == 5)
            continue; // jump to end of loop body

        std::cout << count << '\n';

        ++count; // this statement is never executed after count reaches 5

        // The continue statement jumps to here
    }

    return 0;
}

Ten program ma za zadanie wypisać każdą liczbę od 0 do 9 z wyjątkiem 5. Ale w rzeczywistości wypisuje:

0
1
2
3
4

a następnie przechodzi w nieskończoną pętlę. Gdy count Jest 5, instrukcja if daje w wyniku true, a continue powoduje, że wykonanie przeskakuje na sam dół pętli. Zmienna count nigdy nie jest zwiększana. W rezultacie w następnym przebiegu count jest nadal 5, instrukcja if jest nadal true, a program kontynuuje pętlę w nieskończoność.

Oczywiście już wiesz, że jeśli masz oczywistą zmienną licznikową, powinieneś użyć pętli for , a nie while lub do while pętla.

Debata na temat użycia przerwania i kontynuowania

Wiele podręczników ostrzega czytelników, aby nie używali break i continue w pętlach, zarówno dlatego, że powoduje to przeskakiwanie przepływu wykonywania, jak i dlatego, że może utrudnić śledzenie przepływu logiki. Na przykład break w środku skomplikowanego fragmentu logiki może zostać pominięty lub może nie być oczywiste, w jakich warunkach powinien zostać wywołany.

Jednakże stosowane rozsądnie break i continue może sprawić, że pętle będą bardziej czytelne, zmniejszając liczbę zagnieżdżonych bloków i zmniejszając potrzebę stosowania skomplikowanej logiki pętli.

Rozważmy na przykład następujący program:

#include <iostream>

int main()
{
    int count{ 0 }; // count how many times the loop iterates
    bool keepLooping { true }; // controls whether the loop ends or not
    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;
}

To program używa zmiennej logicznej do kontrolowania kontynuacji pętli, a także zagnieżdżonego bloku, który jest uruchamiany tylko wtedy, gdy użytkownik nie zakończy działania.

Oto wersja łatwiejsza do zrozumienia dzięki zastosowaniu break instrukcji:

#include <iostream>

int main()
{
    int count{ 0 }; // count how many times the loop iterates
    while (true) // loop until user terminates
    {
        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;
}

W tej wersji, używając pojedynczej break instrukcji, uniknęliśmy użycia zmiennej logicznej (i konieczności zrozumienia zarówno jej zamierzonego zastosowania, i gdzie zmienia się jej wartość), instrukcja else i zagnieżdżony blok.Instrukcja

Klasa continue jest najskuteczniej używana na początku pętli for, aby pominąć iteracje pętli, gdy spełniony jest jakiś warunek. Dzięki temu możemy uniknąć zagnieżdżonych bloków.

Na przykład zamiast tego:

#include <iostream>

int main()
{
    for (int count{ 0 }; count < 10; ++count)
    {
        // if the number is not divisible by 4...
        if ((count % 4) != 0) // nested block
        {
                // Print the number
                std::cout << count << '\n';
        }
    }

    return 0;
}

Możemy napisać tak:

#include <iostream>

int main()
{
    for (int count{ 0 }; count < 10; ++count)
    {
        // if the number is divisible by 4, skip this iteration
        if ((count % 4) == 0)
            continue;

        // no nested block needed

        std::cout << count << '\n';
    }

    return 0;
}

Minimalizacja liczby używanych zmiennych i utrzymywanie małej liczby zagnieżdżonych bloków poprawiają zrozumiałość kodu bardziej niż break lub continue szkodzą. Z tego powodu uważamy, że rozsądne użycie break lub continue jest dopuszczalne.

Najlepsza praktyka

Użyj break icontinue, gdy upraszczają one logikę pętli.

Debata na temat stosowania wczesnych zwrotów

Istnieje podobny argument, który można wysunąć w przypadku instrukcji return. Instrukcja return, która nie jest ostatnią instrukcją w funkcji, nazywana jest wczesnym powrotem. Wielu programistów uważa, że ​​należy unikać wczesnych powrotów. Funkcja, która ma tylko jedną instrukcję return na dole, jest prosta — można założyć, że funkcja przyjmie swoje argumenty, wykona dowolną zaimplementowaną logikę i zwróci wynik bez odchyleń. Dodatkowe zwroty komplikują logikę.

Kontrargumentem jest to, że użycie wczesnych zwrotów pozwala funkcji zakończyć natychmiast po jej zakończeniu, co ogranicza konieczność czytania niepotrzebnej logiki i minimalizuje potrzebę warunkowych zagnieżdżonych bloków, co czyni kod bardziej czytelnym.

Niektórzy programiści wybierają złoty środek i używają wczesnych zwrotów na górze funkcji tylko w celu sprawdzenia parametrów (wyłapania błędnych argumentów), a następnie pojedynczego powrotu później.

Nasze stanowisko jest takie, że wczesne powroty są bardziej pomocne niż szkodliwe, ale zdajemy sobie sprawę, że w praktyce jest odrobina sztuki.

Najlepsza praktyka

Wykorzystuj wczesne powroty, gdy upraszczają logikę funkcji.

guest
Twój adres e-mail nie zostanie wyświetlony
Znalazłeś błąd? Zostaw komentarz powyżej!
Komentarze związane z poprawkami zostaną usunięte po przetworzeniu, aby pomóc zmniejszyć bałagan. Dziękujemy za pomoc w ulepszaniu witryny dla wszystkich!
Awatary z https://gravatar.com/ są połączone z podanym adresem e-mail.
Powiadamiaj mnie o odpowiedziach:  
164 Komentarze
Najnowsze
Najstarsze Najczęściej głosowane
Wbudowane opinie
Wyświetl wszystkie komentarze