Ошибка обработки исключений для строк и двойных чисел в C++

Я пытаюсь написать программу для малышей, но у меня обработка исключений работает неправильно. Вместо этого я получаю следующий вывод, показывающий вывод if (DaysNumber < 7) вместо сообщения об ошибке:

У меня есть следующий код (это мой основной файл; у меня есть ссылка на мой репозиторий GitHub для других файлов внизу этого вопроса, если вы хотите увидеть и эти файлы):

#include <iostream>
#include <iomanip>
#include <string>
#include <cstdlib>
#include <stdexcept>
#include "tatertot.h"

using namespace std;

int main() {
    int DaysNumber;
   
    // Asks the user how long it’s been since they ate tater tots
    cout << "How long has it been since you had some yummy tots?" << endl;
    cout << "Enter the number of days: " << endl;
    cin >> DaysNumber;

    // Handles invalid inputs
   try {
       // If it’s been a week (or more) since the user ate tater tots…
        if (DaysNumber >= 7) {
            cout << "Yay! I bet those tots were delicious. Here's a recipe to try next time you want to eat tater tots: " << endl;
            cout << "https://www.allrecipes.com/recipe/236749/tater-tots-nachos/";
            abort();
        }

        // If the user ate tater tots recently…
        else {
            cout << "It. Is. TATER TOT TIME!" << endl;
            cout << "Go enjoy some yummy tots!" << endl;
            cout << "https://www.allrecipes.com/recipe/236749/tater-tots-nachos/";
            abort();
        }

    }
    catch (string e) {
        cin.clear();
        cout << "I'm sorry, but your input is invalid. Could you please try again?" << endl;
        cin >> DaysNumber;
    }

    catch (double e) {
        cin.clear();
        cout << "I'm sorry, but your input is invalid. Could you please try again?" << endl;
        cin >> DaysNumber;
    }
    return 0;
};

Я пробовал использовать другие методы обработки исключений и определять DaysNumber в файле класса и файле средства доступа-мутатора, но безрезультатно. Может быть, мне здесь чего-то не хватает?

Вот ссылка на мой репозиторий GitHub: https://github.com/NEE64/potate-repository

Я не вижу, чтобы вы throw создавали исключение, так как же вы ожидали, что блоки catch будут вызваны?

PaulMcKenzie 28.04.2024 02:45

Спасибо всем, кто предложил помощь и совет! Я обязательно запомню эти советы, когда через пару дней вернусь на занятия в колледже (и когда мне снова захочется написать программу).

NEE64 04.05.2024 19:43
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
1
2
87
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Ответ принят как подходящий

Ваш неверный ввод просто приводит к чтению 0 в переменную. Ничто не вызывает исключений, которые вы пытаетесь перехватить.

Ставьте лайк, посмотрите https://en.cppreference.com/w/cpp/io/basic_istream/operator_gtgt :

Если извлечение не удается (например, если вместо ожидаемой цифры была введена буква), в значение записывается ноль и устанавливается бит ошибки.

Кроме того, стандартная библиотека C++ никогда не генерирует исключений string или double. Я думаю, вы ожидаете, что они будут выданы, когда пользователь вводит строку или двойное число вместо целого числа? C++ не творит подобных чудес.

Вы можете попросить operator>> выдать исключение при неверном вводе. См. метод cin.Exceptions() . Но он выдаст исключение std::ios_base::failure.

Remy Lebeau 28.04.2024 03:05

@RemyLebeau да, можешь. Но я только что перепроверил, он этого не делает - и он говорил о переходе в случай "<7", а не о получении неперехваченного исключения. В каком-то смысле моя стратегия отладки обычно сосредоточена на том, что было сделано, а не на том, что можно было сделать — я смотрю на симптомы, я смотрю на код и пытаюсь понять, смогу ли я объяснить симптомы с помощью кода.

Christian Stieber 28.04.2024 12:09

Структура вашей программы имеет смысл только для людей. Компьютеры слишком тупы, чтобы знать, как начать все сначала, если вы не объясните это явно.

⟶ For each of the following snippets I will list all the required includes. Generally you need only list each include once, all together at the top of the source file.

Пользовательский ввод

Текстовый ввод обычно следует получать в виде полной строки текста, а затем попытаться преобразовать его в то, что вы запрашивали. Это потому, что пользователь всегда будет нажимать Enter после каждого запрошенного ввода!

Пользовательский ввод • Преобразование строки в значение

У меня есть небольшая служебная функция, которую я использую, чтобы попытаться преобразовать вводимые пользователем данные в нужный тип данных. Он возвращает либо успешно преобразованное значение, либо недопустимое состояние.

#include <ciso646>
#include <optional>
#include <sstream>
#include <string>

template <typename T>
auto string_to( const std::string & s )
{
    T value;
    std::istringstream ss( s );
    return ((ss >> value) and (ss >> std::ws).eof())
        ? value
        : std::optional <T> { };
}

Мы можем использовать его следующим образом:

// EXAMPLE
std::cout << "Gimme a Double on the double: ";
std::string s;
getline( std::cin, s );
auto value = string_to <double> ( s );
if (value)
    std::cout << "Yay! " << *value << "\n";
else
    std::cout << "Booo!\n";

Пользовательский ввод • Запрос и получение значения

Это приглашение, получение строки, попытка преобразования и проверка — это обычное дело, которое мы делаем снова и снова. Давайте напишем еще одну вспомогательную функцию, которая сделает все это за нас:

#include <iostream>
#include <string>

template <typename T>
auto ask( const std::string & prompt )
{
    std::cout << prompt;
    std::string s;
    getline( std::cin, s );
    return string_to <T> ( s );
}

Это облегчает нам жизнь:

// EXAMPLE
auto value = ask <int> ( "How many days? " );
if (value)
    std::cout << "Good job! You entered " << *value << " days!\n";
else
    std::cout << "That was not an INTEGER value.\n";

Пользовательский ввод • Подсказка и проверка

Мы можем еще больше улучшить нашу жизнь, написав еще одну небольшую утилиту, которая сделает за нас всю проверку и вернет нужный нам тип данных без неприятной std::optional-обертки:

#include <stdexcept>
#include <string>

template <typename T, typename Verify>
T ask( const std::string & prompt, Verify verify, const std::string & error_message )
{
    auto value = ask <int> ( prompt );
    if (!value or !verify( *value ))
        throw std::runtime_error( error_message );
    return *value;
}

Эта версия ask<>() выдает нам исключение во время выполнения, если функция проверки завершается сбоем. Через секунду мы увидим, как его использовать.

Основная программа

Теперь мы готовы использовать наши удобные вспомогательные функции, чтобы упростить работу с функцией main().

#include <exception>
#include <iostream>

int main()
try
{
    // Ask the user for an integer value
    int daysNumber = ask <int> (
        "How many days has it been since you have had some yummy tots? ",

        // Here is our validation function AND the message
        // to complain with should validation fail.
        []( int value ) { return value >= 0; },
        "Number of days must be an integer value ≥ 0." );

    // At this point we KNOW we have an integer number of days ≥ 0
    if (daysNumber > 7)
    {
        std::cout
            << "Yay! I bet those tots were delicious.\n"
            << "Here's a recipe to try next time you want to eat tater tots:\n"
            << "https://www.allrecipes.com/recipe/236749/tater-tots-nachos/\n";
    }
    else
    {
        std::cout
            << "It. Is. TATER TOT TIME!\n"
            << "Go enjoy some yummy tots!\n"
            << "https://www.allrecipes.com/recipe/236749/tater-tots-nachos/\n";
    }

    // return 0;  // This can be omitted in C++
}

// Here we catch all errors derived from the standard exception
// class. We print the error message and terminate nicely, complete
// with an error code.
catch (const std::exception & e)
{
    std::cerr << e.what() << "\n";
    return 1;
}

На данный момент у нас есть полная программа! Просто вставьте все вышеперечисленное (кроме ПРИМЕРОВ) в файл .cpp, скомпилируйте его и приступайте!

⟶ Here, I did it for you: https://godbolt.org/z/YszzvTdTK

Примечания

Ход выполнения нашей основной программы обычно не должен давать пользователю слишком много возможностей ввести неправильный ввод.

⟶ Save that kind of stuff for interactive UI type programs, such as an editor or something that runs from a menu.

Другими словами, если пользователь вводит что-то неправильно, скажите ему: «Крепкие бобы!» и умереть (с сообщением об ошибке).

Кроме того, использование abort() — это своего рода удар в спину вашей программе, хотя вы ей все еще нравитесь. Предпочитаю красиво завершить программу. (Вы можете сделать это с исключениями.) C++ делает обработку исключений исключительно простой (к сожалению, это каламбур).

Лямбды

Эта странная штука []( int value ) { ... } называется лямбда. Это просто способ написания встроенной безымянной функции. Мы могли бы написать обычную функцию:

bool validate_ge_0( int value )
{
    return value >= 0;
}

И использовал его так:

    int daysNumber = ask <int> (
        "How many days has it been since you have had some yummy tots? ",
        validate_ge_0,  // (this is the name of the validation function)
        "Number of days must be an integer value ≥ 0." );

Петли!

Вы все еще можете делать что-то с циклами. Просто не забудьте указать это явно и дать пользователю возможность уйти. Помните, вы можете написать это так, как вам нравится!

template <typename T, typename Validate>
auto ask_n_times(
    unsigned n,
    const std::string & prompt,
    Validate validate,
    const std::string & error_message )
{
    if (n == 0) n = 1;
    while (n--)
    {
        auto value = ask <T> ( prompt );
        if (value and verify( *value ))
            return *value;
        std::cout << error_message << "\n";
        std::cout << n << " more tries to answer.\n";
    }
    throw std::runtime_error( error_message.c_str() );
}

А потом:

int main()
{
    int evenNumber = ask_n_times <int> (
        3,
        "Even number? ",
        []( int value ) { return !(value & 1); },
        "That was not an even number." );
    ...

Это должно дать вам хорошее представление о том, как организовать ваш код. Помните, что повторяющиеся или общие задачи могут быть обернуты небольшими вспомогательными функциями, и все, что вы хотите сделать несколько раз, должно выполняться в явном цикле. В противном случае программа выполняется так же, как вы ее читаете, сверху вниз, начиная с функции main().

Удачи!

Другие вопросы по теме

Похожие вопросы