C++ 如何创建异常?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/16182781/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
How to create exceptions?
提问by Charlie M
So I have an upcoming assignment dealing with exceptions and using them in my current address book program that most of the homework is centered around. I decided to play around with exceptions and the whole try catch thing, and using a class design, which is what I will eventually have to do for my assignment in a couple of weeks. I have working code that check the exception just fine, but what I want to know, is if there is a way to standardize my error message function, (i.e my what() call):
所以我有一个即将到来的任务处理异常并在我当前的地址簿程序中使用它们,大多数家庭作业都围绕着这些程序。我决定尝试处理异常和整个 try catch 事情,并使用类设计,这就是我在几周内最终必须为我的作业做的事情。我有工作代码可以很好地检查异常,但我想知道的是,是否有一种方法可以标准化我的错误消息函数,(即我的 what() 调用):
Here s my code:
这是我的代码:
#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 I would like to be able to do is make it so my what() function can return a value dependent on what type of error is being called on. So for instance, if I were calling up an error that looked a the top number,(a), to see if it was a zero, and if it was, it would then set the message to say that "you can't have a numerator of zero", but still be inside the what() function. Here's an example:
我希望能够做的是让我的 what() 函数可以返回一个取决于正在调用的错误类型的值。因此,例如,如果我调用一个看起来是最高数字 (a) 的错误,以查看它是否为零,如果是,它将设置消息说“你不能有分子为零”,但仍然在 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
}
This obviously wouldn't work, but is there a way to make it so I'm not writing a different function for each error message?
这显然行不通,但是有没有办法做到这一点,所以我不会为每个错误消息编写不同的函数?
回答by Baltasarq
Your code contains a lot of misconceptions. The short answer is yes, you can change what()in order to return whatever you want. But let's go step by step.
您的代码包含很多误解。简短的回答是肯定的,您可以更改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;
In the first place, runtime_error, derived from exception, is the adviced exception class to derive from. This is declared in the stdexcept header. You only have to initialize its constructor with the message you are going to return in the what()method.
首先,runtime_error从 派生的exception是建议派生的异常类。这是在 stdexcept 标头中声明的。你只需要用你要在what()方法中返回的消息来初始化它的构造函数。
Secondly, you should appropriately name your classes. I understand this is just a test, but a descriptive name will always help to read and understand your code.
其次,你应该适当地命名你的类。我知道这只是一个测试,但描述性名称总是有助于阅读和理解您的代码。
As you can see, I've changed the constructor in order to accept the numbers to divide that provoked the exception. You did the test in the exception... well, I've respected this, but as a static function which can be invoked from the outside.
如您所见,我更改了构造函数以接受引发异常的除法数字。您在异常中进行了测试......好吧,我尊重这一点,但作为可以从外部调用的静态函数。
And finally, the what()method. Since we are dividing two numbers, it would be nice to show that two numbers that provoked the exception. The only way to achieve that is the use of ostringstream. Here we make it static so there is no problem of returning a pointer to a stack object (i.e., having cnvta local variable would introduce undefined behaviour).
最后,what()方法。由于我们将两个数字相除,最好显示两个引发异常的数字。实现这一目标的唯一方法是使用 ostringstream。在这里,我们将其设为静态,因此不存在返回指向堆栈对象的指针的问题(即,具有cnvt局部变量会引入未定义的行为)。
The rest of the program is more or less as you listed it in your question:
该程序的其余部分或多或少如您在问题中列出的那样:
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;
}
As you can see, I've removed your return main()instruction. It does not make sense, since you cannot call main()recursively. Also, the objective of that is a mistake: you'd expect to retry the operation that provoked the exception, but this is not possible, since exceptions are not reentrant. You can, however, change the source code a little bit, to achieve the same effect:
如您所见,我已删除您的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;
}
As you can see, in case of an error the execution follows until a "proper" division is entered.
如您所见,如果出现错误,将继续执行,直到输入“正确”的除法为止。
Hope this helps.
希望这可以帮助。
回答by Shashank Chaurasia
You can create your own exception class for length error like this
您可以为这样的长度错误创建自己的异常类
class MyException : public std::length_error{
public:
MyException(const int &n):std::length_error(to_string(n)){}
};
回答by John Kemp
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();
}
You should always use std::exception&e. so do
您应该始终使用 std::exception&e。也是
catch(std::exception & e)
{
cout<<e.what();
}
回答by Arthur P. Golubev
You should consider a hierarchy of classes.
您应该考虑类的层次结构。
The reason for it might not be obvious when trying to use exceptions just for transferring a string, but actual intent of using exceptions should a mechanism for advanced handling of exceptional situations. A lot of things are being done under the hood of C++ runtime environment while call stack is unwound when traveling from 'throw' to corresponded 'catch'.
当尝试仅将异常用于传输字符串时,其原因可能并不明显,但使用异常的实际意图应该是一种高级处理异常情况的机制。许多事情都是在 C++ 运行时环境的引擎盖下完成的,而调用堆栈在从“抛出”到相应的“捕获”的过程中会展开。
An example of the classes could be:
类的一个例子可能是:
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 {
{
// ....
}
};
- Providing different classes for the errors, you give developers a chance to handle different errors in particular ways (not just display an error message)
- Providing a base class for the types of error, allows developers to be more flexible - be as specific as they need.
- 为错误提供不同的类,您可以让开发人员有机会以特定方式处理不同的错误(不仅仅是显示错误消息)
- 为错误类型提供基类,允许开发人员更灵活 - 根据需要尽可能具体。
In some cases, they need to be specific
在某些情况下,它们需要具体
} catch (const ZeroDividerError & ex) {
// ...
} catch (const DivisionError & ex) {
in others, don't
在其他情况下,不要
} catch (const DivisionError & ex) {
As for some additional details,
至于一些额外的细节,
- You should not create objects of your exceptions before throwing in the manner you did. Regardless your intention, it is just useless - anyway, you are working with a copy of the object in catch section (don't be confused by access via reference)
- Using a const reference would be a good style
catch (const testException &te)unless you really need a non-constant object.
- 在以您所做的方式抛出之前,您不应创建异常的对象。无论您的意图如何,它都是无用的 - 无论如何,您正在使用 catch 部分中的对象副本(不要被通过引用访问所迷惑)
catch (const testException &te)除非您确实需要非常量对象,否则使用 const 引用将是一种很好的风格。

