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

Таким образом, у меня есть предстоящее задание, касающееся исключений и использования их в моей текущей программе адресной книги, вокруг которой сосредоточено большинство домашних заданий. Я решил поиграть с исключениями и попытаться поймать все, и использовать дизайн класса, что я в конечном итоге должен сделать для своего задания через пару недель. У меня есть рабочий код, который прекрасно проверяет исключение, но я хочу знать, есть ли способ стандартизировать мою функцию сообщения об ошибке (т.е. вызов 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 objectint 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
}

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

20

Решение

Ваш код содержит много заблуждений. Короткий ответ: да, вы можете изменить 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 локальная переменная будет вводить неопределенное поведение).

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

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;

}

Как видите, в случае ошибки выполнение следует до тех пор, пока не будет введено «правильное» деление.

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

36

Другие решения

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

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

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&е. Ну действуй

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

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

Причина этого может быть не очевидна при попытке использовать исключения только для передачи строки, но реальное намерение использования исключений должно заключаться в механизме расширенной обработки исключительных ситуаций. Многое делается в среде выполнения C ++, в то время как стек вызовов разматывается при переходе от throw к соответствующему 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 (не путайте доступ по ссылке)
  • Использование константной ссылки было бы хорошим стилем catch (const testException &te) если вам действительно не нужен непостоянный объект.
1