March 11th, 2020

Ошибка в программе симуляции работы лифтов

В 13-й главе учебника Лафоре в качестве примера многофайловой программы приведена программа симуляции автоматической работы четырех лифтов здания в 20 этажей.

Вот, собственно, проблемный код (стр.626) в файле elev.cpp:
void building::record_floor_reqs() // получение запросов от пассажиров с этажей
{
    char ch = 'x';                 // символ для ввода
    
    // ...
    
    cout << "Нажмите [Enter] для вызова лифта: ";
    if( !kbhit() )                 // ожидание нажатия (должен быть CR,
                                   // возврат каретки)
        return;
    cin.ignore(10, '\n');
    if(ch=='\x1B')                 // при нажатии Esc — выход из программы
        exit(0);

    // ...

}
(Я заменил многоточиями код, который не нужен для понимания ошибки.)

Сначала мне показалось подозрительным, что ожидание нажатия клавиши на клавиатуре сделано ветвлением if. По идее, должен быть цикл.

Но, как оказалось, цикл там есть. Сам этот метод вызывается в бесконечном цикле в другом файле (elev_app.cpp, стр.633):
#include "elev.h" // описатели классов building и elevator

int main()
{
    building theBuilding;

    while(true)
    {
        theBuilding.master_tick();       // запустить такты всех лифтов
        wait(1000);                      // пауза
        theBuilding.record_floor_reqs(); // получить запросы с этажей
    }
    
    return 0;
}
В этом бесконечном цикле программа постоянно вызывает метод building::record_floor_reqs(), который отображает на экране надпись «Нажмите [Enter] для вызова лифта» и проверяет в ветвлении if, не нажал ли пользователь какую-нибудь клавишу на клавиатуре (программа запрашивает нажатие клавиши Enter, но на самом деле можно нажать любую клавишу).

Если пользователь ничего не нажал, работа метода building::record_floor_reqs() завершается и программа возвращается в бесконечный цикл. Затем всё повторяется.

Если пользователь нажал какую-нибудь клавишу, продолжается работа указанного метода и программа переходит к следующим инструкциям:
    cin.ignore(10, '\n');
    if(ch=='\x1B')                 // при нажатии Esc — выход из программы
        exit(0);

Здесь уже видно ошибку. Очевидно, что автор программы предполагал, что в переменной ch окажется значение клавиши, нажатой на клавиатуре. При этом, если бы это оказалась клавиша Esc, то программа должна была завершить свою работу. При нажатии Enter или любой другой клавиши (кроме Esc) метод должен продолжить свою работу.

Однако, в программе отсутствует присвоение переменной ch значения (кроме ее инициализации). Поэтому выхода из такой программы не произойдет вообще.

Вместо совершенно лишней здесь инструкции cin.ignore(10, '\n'); в программу следует добавить инструкцию ch = _getch(); (подробнее про эту функцию). Наверное, автор скопировал откуда-то неправильную инструкцию в спешке. В итоге должно получиться следующее:
void building::record_floor_reqs() // получение запросов от пассажиров с этажей
{
    char ch = 'x';                 // символ для ввода
    
    // ...
    
    cout << "Нажмите [Enter] для вызова лифта: ";
    if( !kbhit() )                 // ожидание нажатия (должен быть CR,
                                   // возврат каретки)
        return;
    ch = _getch();
    if(ch=='\x1B')                 // при нажатии Esc — выход из программы
        exit(0);

    // ...

}

Полный текст данной программы (она состоит из 4-х файлов).