Урок №68. Оператор switch

  Юрій  | 

  Оновл. 18 Чер 2020  | 

 103

У цьому уроці ми розглянемо ще один оператор управління потоком виконання програми — оператор switch, а також навіщо його використовувати і як це робити ефективно.

Навіщо використовувати оператор switch?

Хоча ми і можемо використовувати відразу декілька операторів if/else разом — читається і виглядає це не дуже. Наприклад:

Використання розгалуження if/else для перевірки значення однієї змінної — практика поширена, але C++ надає альтернативний і більш ефективний умовний оператор розгалуження — switch. Ось та ж програма, що вище, але вже з використанням оператора switch:

Загальна ідея операторів switch проста: вираз оператора switch (наприклад, switch(color)) має генерувати значення, а кожен випадок (або ще “case“) перевіряє це значення на відповідність. Якщо case збігається з виразом switch-а, то виконуються інструкції під відповідним кейсом. Якщо case не відповідає виразу switch-а, то виконуються інструкції після кейса default (якщо його взагалі вказали).

Через свою реалізацію, оператори switch зазвичай більш ефективні, ніж ланцюжки if/else. Давайте розглянемо це більш детально.

Оператор switch

Спочатку пишеться ключове слово switch, за яким слідує вираз, з яким ми хочемо працювати. Зазвичай цим виразом є окрема змінна, але це може бути і щось більш складне, наприклад, nX + 2 чи nX − nY. Єдине обмеження до цього виразу — воно повинно бути інтегрального типу даних (тобто char, short, int, long, long long чи enum). Змінні типу з плаваючою крапкою або НЕ інтегральні типи використовуватися не можуть.

Після виразу switch ми оголошуємо блок. Усередині блоку ми використовуємо лейбли (англ. “labels“) для визначення всіх значень, які ми хочемо перевіряти на відповідність з виразом. Існує два типи лейблів: case і default.

Лейбли case

Перший вид лейблів — це case (або просто “кейс“), який оголошується з використанням ключового слова case і має константний вираз. Константний вираз — це той вираз, який генерує константне значення, іншими словами це літерал (наприклад, 5), перерахування (наприклад, COLOR_RED) чи константа (наприклад, змінна x, яка була оголошена з ключовим словом const).

Константний вираз, що знаходиться після ключового слова case, перевіряється на рівність з виразом, що знаходяться біля ключового слова switch. Якщо вони збігаються, то тоді виконується код під відповідним кейсом.

Варто відзначити, що всі вирази case повинні генерувати унікальні значення. Тобто ви не зможете зробити наступне:

Можна використовувати відразу декілька кейсів для одного виразу. Наступна функція використовує декілька кейсів для перевірки відповідності параметру p числу з ASCII-таблиці:

У випадку, якщо p є числом з ASCII-таблиці, то виконуватися буде перший стейтмент після кейса: return true;.

Лейбл default

Другий тип лейбла — це лейбл за замовчуванням (так званий “default case“), який оголошується з використанням ключового слова default. Код під цим лейблом виконується, якщо жоден з кейсів не відповідає виразу switch. Лейбл за замовчуванням є необов’язковим. В одному switch може бути тільки один default. Зазвичай його оголошують останнім в блоці switch.

В наведеному вище прикладі, якщо p не є числом з ASCII-таблиці, то тоді виконується лейбл за замовчуванням і повертається false.

switch і fall-through

Одна з найбільш каверзних речей в switch — це послідовність виконання коду. Коли відбувся збіг з кейсом (або виконується default), то виконання починається з першого стейтменту, який знаходиться після відповідного кейса і триває до тих пір, поки не буде виконано одну з наступних умов завершення:

   Досягнуто кінець блоку switch.

   Виконується оператор return.

   Виконується оператор goto.

   Виконується оператор break.

Зверніть увагу, якщо жодної з цих умов завершення не буде, то виконуватися будуть всі кейси після того кейса, який співпав з виразом switch. Наприклад:

Результат:

2
3
4
5

А ось це точно не те, що нам потрібно! Коли виконання переходить з одного кейсу в наступний, то це називається fall-through. Програмісти майже ніколи не використовують fall-through, тому в окремих випадках, коли це все-таки використовується — програміст залишає коментар, в якому повідомляє, що fall-through є навмисним.

switch і оператор break

Оператор break (оголошений з використанням ключового слова break) повідомляє компілятору, що ми вже зробили все, що хотіли з певним switch-ом (або циклом while, do while чи for) і більше не маємо наміру з ним працювати. Коли компілятор зустрічає оператор break, то виконання коду переходить зі switch-а на наступний рядок після блоку switch. Розглянемо приклад вище, але вже з коректно вставленими операторами break:

Оскільки другий кейс відповідає виразу switch, то виводиться 2, і оператор break завершує виконання блоку switch. Решта кейсів пропускаються.

Попередження: Не забувайте використовувати оператор break в кінці кожного кейсу. Його відсутність — одна з найпоширеніших помилок новачків!

Декілька стейтментів всередині блоку switch

Ще одна особливість switch полягає в тому, що ви можете використовувати декілька стейтментів під кожним кейсом, не визначаючи новий блок:

Оголошення змінної і її ініціалізація всередині кейсу

Ви можете оголосити, але не ініціалізувати змінні всередині блоку case:

Зверніть увагу, що, хоча змінна z була визначена в першому кейсі, вона також використовується і в другому кейсі. Всі кейси вважаються частиною однієї і тієї ж області видимості, тому, оголосивши змінну в одному кейсі, ми можемо спокійно використовувати її без оголошення і в інших кейсах.

Це може здатися трохи нелогічним, тому давайте розглянемо це детальніше. Коли ми визначаємо локальну змінну, наприклад, int y;, то змінна не створюється в цій точці — вона фактично створюється на початку блоку, в якому її оголосили. Однак, в програмі її не видно до точки оголошення. Саме оголошення не виконується, воно просто повідомляє компілятору, що змінна вже може використовуватися в коді. Тому змінна, оголошена в одному кейсі, може використовуватися в іншому кейсі, навіть якщо кейс, що оголошує змінну, ніколи не виконається.

Проте ініціалізація змінних безпосередньо в кейсах заборонена і викличе помилку компіляції. Це пов’язано з тим, що ініціалізація змінної вимагає виконання, а case, що містить ініціалізацію, — може ніколи не виконатися!

Якщо в кейсі потрібно оголосити і/або ініціалізувати нову змінну, то це найкраще зробити, використовуючи блок стейтментів всередині кейсу:

Правило: Якщо потрібно ініціалізувати і/або оголосити змінні всередині кейсу, то використовуйте блоки стейтментів.

Тест

Завдання №1

Напишіть функцію calculate(), яка приймає дві змінні типу int і одну змінну типу char, яка представляє одну з наступних математичних операцій: +, -, *, / або % (залишок від числа). Використайте switch для виконання відповідної математичної операції над цілими числами і поверніть результат. Якщо в функцію передається недійсний математичний оператор, то функція повинна виводити помилку. З оператором ділення виконуйте цілочисельне ділення.

Відповідь №1

Завдання №2

Визначте перерахування (або клас enum) Animal, яке містить наступних тварин:

   pig

   chicken

   goat

   cat

   dog

   ostrich

Напишіть функцію getAnimalName(), яка приймає Animal в якості параметра і використовує switch для повернення типу тварини в вигляді рядка. Напишіть ще одну функцію printNumberOfLegs(), яка використовує switch для виведення кількості лапок відповідного типу тварини. Переконайтеся, що обидві функції мають кейс default, який виводить повідомлення про помилку. Викличте printNumberOfLegs() в main(), використовуючи в якості параметрів cat і chicken.

Приклад результату виконання програми:

A cat has 4 legs.
A chicken has 2 legs.

Відповідь №2

Оцінити статтю:

1 Зірка2 Зірки3 Зірки4 Зірки5 Зірок (1 оцінок, середня: 5,00 з 5)
Loading...

Залишити відповідь

Ваш E-mail не буде опублікований. Обов'язкові поля відмічені *