7.8 – Инструкции do while
Рассмотрим случай, когда мы хотим показать пользователю меню и попросить его сделать выбор; и, если пользователь сделает неверный выбор, спросить его снова. Ясно, что меню и выбор должны находиться внутри какого-то цикла (чтобы мы могли продолжать спрашивать пользователя, пока он не введет правильное значение), но какой цикл мы должны выбрать?
Поскольку цикл while
вычисляет условие заранее, он будет неудобен. Решить проблему можно так:
#include <iostream>
int main()
{
// selection должна быть объявлена вне цикла while, чтобы мы могли использовать ее позже
int selection{ 0 };
while (selection != 1 && selection != 2 &&
selection != 3 && selection != 4)
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
}
// здесь что-то делаем с selection
// например, оператор switch
std::cout << "You selected option #" << selection << "\n";
return 0;
}
Но это работает только потому, что наше начальное значение 0 для selection
не входит в набор допустимых значений (1, 2, 3 или 4). Что, если бы 0 был корректным выбором? Нам пришлось бы выбрать другой инициализатор для представления «недопустимого» значения – и теперь мы вводим в наш код магические числа (4.13 – Литералы).
Вместо этого мы могли бы добавить новую переменную для отслеживания корректности, например:
#include <iostream>
int main()
{
int selection { 0 };
bool invalid { true }; // новая переменная только для выхода из цикла
while (invalid)
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
invalid = (selection != 1 && selection != 2 &&
selection != 3 && selection != 4);
}
// здесь что-то делаем с selection
// например, оператор switch
std::cout << "You selected option #" << selection << "\n";
return 0;
}
Это позволяет избежать использования магического числа, но вводит новую переменную, чтобы гарантировать, что цикл запускается минимум один раз, что добавляет сложности и возможности дополнительных ошибок.
Инструкции do while
Чтобы помочь решить проблемы, подобные описанным выше, C++ предлагает оператор do
-while
:
do
инструкция;
while (условие);
Оператор do while
– это конструкция цикла, которая работает так же, как цикл while
, за исключением того, что инструкция всегда выполняется хотя бы один раз. После выполнения инструкции цикл do
-while
проверяет условие. Если условие истинно, последовательность выполнения возвращается к началу цикла do
-while
и выполняет его снова.
Вот наш пример выше с использованием цикла do
-while
вместо цикла while
:
#include <iostream>
int main()
{
// selection должна быть объявлена вне do-while, чтобы мы могли использовать ее позже
int selection{};
do
{
std::cout << "Please make a selection: \n";
std::cout << "1) Addition\n";
std::cout << "2) Subtraction\n";
std::cout << "3) Multiplication\n";
std::cout << "4) Division\n";
std::cin >> selection;
}
while (selection != 1 && selection != 2 &&
selection != 3 && selection != 4);
// здесь что-то делаем с selection
// например, оператор switch
std::cout << "You selected option #" << selection << "\n";
return 0;
}
Таким образом, мы избежали как магических чисел, так и дополнительных переменных.
В приведенном выше примере стоит обсудить то, что переменная selection
должна быть объявлена вне блока do
. Если бы переменная selection
была объявлена внутри блока do
, она была бы уничтожена при завершении блока do
, что происходит до того, как будет вычислено условие. Но эта переменная нужна нам в условном выражении while
– следовательно, переменная selection
должна быть объявлена вне блока do
(даже если она не будет использоваться в теле функции позже).
На практике циклы do
-while
используются нечасто. Расположение условного выражения в нижней части цикла скрывает условие цикла, что может привести к ошибкам. В результате многие разработчики рекомендуют вообще избегать циклов do
-while
. Мы займем более мягкую позицию и выступим за то, чтобы при равном выборе предпочтение было отдано циклам while
, а не do
-while
.
Лучшая практика
При равном выборе отдавайте предпочтение циклам while
, вместо do
-while
.