Do czego służy switch
switch porównuje jedną wartość z listą stałych opcji i wykonuje pasującą gałąź. Jeśli łapiesz się na pisaniu długiego łańcucha if/else if, który w kółko sprawdza tę samą zmienną z różnymi stałymi, switch wyraża ten zamiar czytelniej i często kompiluje się do szybszej tablicy skoków.
Wykonuje wyłącznie sprawdzenia równości ze stałymi znanymi w czasie kompilacji, na wartości typu całkowitoliczbowego. Nie sprawdzi zakresów, nie porówna napisów i nie połączy warunków. W każdym z tych przypadków zostań przy if/else.
Podstawowy switch
Przekazujesz do switch wartość, a potem wypisujesz etykiety case dla interesujących cię wartości. Każda gałąź kończy się instrukcją break:
Wartość 3 pasuje do case 3:, więc wypisuje się "Wednesday", a break wyskakuje z instrukcji switch. Gałąź default łapie wszystko inne: wykonuje się, gdy żaden case nie pasuje. Jest opcjonalna, ale dodawaj ją, chyba że masz pewność, że obsłużono każdą wartość.
Zauważ, że etykiety case to gołe stałe z dwukropkiem, a nie warunki. Piszesz case 3:, nigdy case day == 3:.
break i fall-through
To najważniejsza pułapka instrukcji switch w C++. Po dopasowaniu case wykonanie nie zatrzymuje się na następnym case: biegnie dalej, aż trafi na break albo zamykającą klamrę. Zobacz, co się dzieje, gdy brakuje instrukcji break:
Można by się spodziewać samego "one". Zamiast tego wypisują się wszystkie cztery linie: dopasowanie case 1: wchodzi do instrukcji switch w tym miejscu, a potem przechodzi przez każdą etykietę poniżej. Dodaj break; po każdej gałęzi, a dostaniesz tylko tę jedną linię, o którą chodziło. Brak break to klasyczne źródło błędów typu "dlaczego mój switch wykonuje za dużo kodu?".
Celowe fall-through do grupowania przypadków
Fall-through nie zawsze jest błędem: to idiomatyczny sposób, żeby kilka przypadków dzieliło jedno ciało. Zostaw przypadki puste (bez instrukcji i bez break), a wszystkie przejdą do następnego bloku:
'A', 'B' i 'C' trafiają do tej samej linii "Pass", bo dwa pierwsze przypadki są puste i przechodzą do trzeciego. To czysty i zamierzony kod. Gdy naprawdę chcesz przejść dalej po wykonaniu jakiegoś kodu, opisz to komentarzem albo, od C++17, atrybutem [[fallthrough]];, który mówi kompilatorowi "tak, tak miało być" i wycisza ostrzeżenia o fall-through.
switch na typach enum
switch naturalnie współpracuje z typami wyliczeniowymi, bo enum to właśnie "jedna z ustalonego zbioru wartości". Kompilatory potrafią też ostrzec, gdy zapomnisz obsłużyć któryś z enumeratorów:
Przy enum class z zakresem musisz kwalifikować każdą etykietę (Direction::East). Wszystkie enumeratory są obsłużone, więc default nie jest potrzebny, a wiele kompilatorów ostrzeże cię, gdy później dodasz piąty kierunek i zapomnisz o jego przypadku. Ta pomoc kompilatora to ważny powód, by przy typach enum wybierać switch zamiast łańcuchów if/else.
Pułapka: deklaracje wewnątrz case
Nie możesz zadeklarować zmiennej z inicjalizatorem w jednym przypadku i mieć jej widocznej w pozostałych bez nadania jej własnego zakresu. To częsty błąd kompilacji:
switch (x) {
case 1:
int n = 10; // błąd: skok do case 2 pomija tę inicjalizację
cout << n;
break;
case 2:
cout << "two";
break;
}
Kompilator to odrzuca, bo wejście do case 2: pominęłoby inicjalizację n, podczas gdy n nadal byłoby w zasięgu. Rozwiązanie to otoczenie ciała przypadku własnymi klamrami, dzięki czemu zmienna dostaje osobny blok:
Gdy case potrzebuje własnej zmiennej lokalnej, daj mu klamry. Dzięki temu zmienna nie wycieka też do przypadków poniżej.
Dalej: pętla for
switch i if pozwalają programowi jednorazowo wybrać, który kod wykonać. Wiele zadań wymaga jednak robienia tego samego wiele razy: liczenia, przechodzenia po liście, powtarzania, aż zmieni się warunek. Do tego służy pętla for i to ona jest tematem następnej strony.
Najczęściej zadawane pytania
Kiedy używać switch zamiast if-else w C++?
Używaj switch, gdy porównujesz jedną wartość całkowitoliczbową z wieloma stałymi opcjami, na przykład wyborem z menu, numerem dnia albo wartością enum. Czyta się to wyraźniej niż długą drabinkę if/else if, a kompilator może zoptymalizować wybór gałęzi. Zostań przy if/else, gdy warunki obejmują zakresy (x > 10), liczby zmiennoprzecinkowe, napisy lub kilka zmiennych: switch nie obsłuży żadnego z tych przypadków.
Po co jest break w instrukcji switch w C++?
Gdy któryś case zostanie dopasowany, C++ wykonuje kolejne przypadki, dopóki nie trafi na break albo na koniec instrukcji switch. To zjawisko nazywa się fall-through (przechodzenie dalej). break je zatrzymuje. Brak break to klasyczny błąd: dopasowujesz case 1: i przypadkiem wykonujesz też kod dla case 2, case 3 oraz default.
Na jakich typach można używać switch w C++?
Tylko na typach całkowitych i wyliczeniowych: int, char, short, long, bool, enum z zakresem lub bez oraz na wszystkim, co się do nich konwertuje. Nie użyjesz switch na double, float ani std::string: w takich przypadkach sięgnij po if/else. Każda etykieta case musi być stałą znaną w czasie kompilacji.