Подтвердить что ты не робот

Как создавать исключения?

Итак, у меня есть задание, связанное с исключениями и использующее их в моей текущей программе адресной книги, в которой сосредоточено большинство домашних заданий. Я решил поиграть с исключениями и всей попыткой поймать вещь и использовать классный дизайн, и это то, что мне в конечном итоге придется сделать для моего задания через пару недель. У меня есть рабочий код, который отлично проверяет исключение, но я хочу знать, есть ли способ стандартизировать мою функцию сообщения об ошибке (например, мой вызов what()):

Вот мой код:

#include <iostream>
#include <exception>
using namespace std;


class testException: public exception
{
public:
  virtual const char* what() const throw() // my call to the std exception class function (doesn't nessasarily have to be virtual).
  {
  return "You can't divide by zero! Error code number 0, restarting the calculator..."; // my error message
  }

  void noZero();

}myex;  //<-this is just a lazy way to create an object



int main()
{
void noZero();
int a, b;

cout << endl;

cout << "Enter a number to be divided " << endl;

cout << endl;

cin >> a;

cout << endl;

cout << "You entered " << a << " , Now give me a number to divide by " << endl;

cin >> b;

try
{    
    myex.noZero(b); // trys my exception from my class to see if there is an issue
}
catch(testException &te) // if the error is true, then this calls up the eror message and restarts the progrm from the start.
{
    cout << te.what() << endl;
    return main();
}

cout <<endl;

cout << "The two numbers divided are " << (a / b) << endl;  // if no errors are found, then the calculation is performed and the program exits.

return 0;

}

  void testException::noZero(int &b) //my function that tests what I want to check
  { 
    if(b == 0 ) throw myex;  // only need to see if the problem exists, if it does, I throw my exception object, if it doesn't I just move onto the regular code.
  }

То, что я хотел бы сделать, это сделать так, чтобы моя функция what() возвращала значение, зависящее от того, какой тип ошибки вызывается. Так, например, если бы я вызывал ошибку, которая выглядела как верхний номер, (а), чтобы увидеть, была ли она нулевой, и если бы это было так, она бы установила сообщение, что "у вас не может быть числитель нуля", но все еще находится внутри функции what(). Вот пример:

  virtual const char* what() const throw() 
  if(myex == 1)
  {
      return "You can't have a 0 for the numerator! Error code # 1 "
  }
  else

  return "You can't divide by zero! Error code number 0, restarting the calculator..."; // my error message
  }

Это явно не сработает, но есть ли способ сделать это, поэтому я не пишу другую функцию для каждого сообщения об ошибке?

4b9b3361

Ответ 1

В вашем коде содержится много заблуждений. Короткий ответ - да, вы можете изменить what(), чтобы вернуть все, что захотите. Но отпустите шаг за шагом.

#include <iostream>
#include <exception>
#include <stdexcept>
#include <sstream>
using namespace std;


class DivideByZeroException: public runtime_error {
public:

  DivideByZeroException(int x, int y)
    : runtime_error( "division by zero" ), numerator( x ), denominator( y )
    {}

  virtual const char* what() const throw()
  {
    cnvt.str( "" );

    cnvt << runtime_error::what() << ": " << getNumerator()
         << " / " << getDenominator();

    return cnvt.str().c_str();
  }

  int getNumerator() const
    { return numerator; }

  int getDenominator() const
    { return denominator; }

  template<typename T>
  static T divide(const T& n1, const T& n2)
    {
        if ( n2 == T( 0 ) ) {
            throw DivideByZeroException( n1, n2 );
        } 

        return ( n1 / n2 );
    }

private:
    int numerator;
    int denominator;

    static ostringstream cnvt;
};

ostringstream DivideByZeroException::cnvt;

Во-первых, runtime_error, полученный из exception, является рекомендуемым классом исключений для вывода. Это объявляется в заголовке stdexcept. Вам нужно только инициализировать свой конструктор сообщением, которое вы собираетесь вернуть в методе what().

Во-вторых, вы должны соответствующим образом называть свои классы. Я понимаю, что это всего лишь тест, но описательное имя всегда поможет прочитать и понять ваш код.

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

И, наконец, метод what(). Поскольку мы делим два числа, было бы неплохо показать, что два числа, вызвавшие исключение. Единственный способ добиться этого - использование ostringstream. Здесь мы делаем его статическим, поэтому нет проблемы с возвратом указателя на объект стека (т.е. С cnvt локальная переменная вводит поведение undefined).

Остальная часть программы более или менее указана в вашем вопросе:

int main()
{
int a, b, result;

cout << endl;

cout << "Enter a number to be divided " << endl;

cout << endl;

cin >> a;

cout << endl;

cout << "You entered " << a << " , Now give me a number to divide by " << endl;

cin >> b;

try
{    
        result = DivideByZeroException::divide( a, b );

    cout << "\nThe two numbers divided are " << result << endl;
}
catch(const DivideByZeroException &e)
{
    cout << e.what() << endl;
}

return 0;

}

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

int main()
{
int a, b, result;
bool error;

do  {
    error = false;

    cout << endl;

    cout << "Enter a number to be divided " << endl;

    cout << endl;

    cin >> a;

    cout << endl;

    cout << "You entered " << a << " , Now give me a number to divide by " << endl;

    cin >> b;

    try
    {    
        result = DivideByZeroException::divide( a, b ); // trys my exception from my class to see if there is an issue

        cout << "\nThe two numbers divided are " << result << endl;
    }
    catch(const DivideByZeroException &e) // if the error is true, then this calls up the eror message and restarts the progrm from the start.
    {
        cout << e.what() << endl;
        error = true;
    }
} while( error );

return 0;

}

Как вы можете видеть, в случае ошибки выполнение выполняется до тех пор, пока не будет введено "правильное" деление.

Надеюсь, что это поможет.

Ответ 2

class zeroNumerator: public std::exception
{
    const char* what() const throw() { return "Numerator can't be 0.\n"; }
};

//...

try
{
  myex.noZero(b); // trys my exception from my class to see if there is an issue
  if(myex==1)
  {
     throw zeroNumerator(); // This would be a class that you create saying that you can't have 0 on the numerator
  }

}
catch(testException &te) 
{
   cout << te.what() << endl;
   return main();
}

Вы всегда должны использовать std:: exception & e. так что

catch(std::exception & e)
{
  cout<<e.what();
 }

Ответ 3

Вы можете создать свой собственный класс исключения для ошибки длины, подобной этой

class MyException : public std::length_error{
public:
  MyException(const int &n):std::length_error(to_string(n)){}
};

Ответ 4

Вы должны рассмотреть иерархию классов.

Причиной этого может быть неясно при попытке использовать исключения только для передачи строки, но фактическое намерение использовать исключения должно иметь механизм для расширенной обработки исключительных ситуаций. Многие вещи выполняются под капотом среды выполнения С++, в то время как стек вызовов разматывается при переходе от "броска" к соответствующему "catch".

Примером классов может быть:

class DivisionError : public std::exception {
public:
    DevisionError(const int numerator, const int divider)
        :numerator(numerator)
        , divider(divider)
    {
    }
    virtual const char* what() const noexcept {
        // ...
    }
    int GetNumerator() const { return numerator; }
    int GetDevider() const { return divider; }
private:
    const int numerator;
    const int divider;
};


class BadNumeratorError : public DivisionError {
public:
    BadNumeratorError(int numerator, int divider)
        : DivisionError(numerator, divider) 
    {
    }
    virtual const char* what() const noexcept {
    {
        // ...
    }
};


class ZeroDeviderError : public DivisionError {
public:
    ZeroDeviderError(int numerator, int divider)
        : DivisionError(numerator, divider)
    {
    }
    virtual const char* what() const noexcept {
    {
        // ....
    }
};
  • Предоставляя разные классы для ошибок, вы даете разработчикам возможность обрабатывать различные ошибки определенным образом (а не просто выводить сообщение об ошибке)
  • Предоставление базового класса для типов ошибок позволяет разработчикам быть более гибкими - быть как можно более конкретными.

В некоторых случаях они должны быть конкретными

} catch (const ZeroDividerError & ex) {
// ...
} catch (const DivisionError & ex) {

в других, не

} catch (const DivisionError & ex) {

Что касается некоторых дополнительных деталей,

  • Вы не должны создавать объекты своих исключений, прежде чем бросать так, как вы это делали. Независимо от ваших намерений, это просто бесполезно - во всяком случае, вы работаете с копией объекта в секции catch (не путайте доступ через ссылку)
  • Использование ссылки const будет хорошим стилем catch (const testException &te), если вам действительно нужен непостоянный объект.