如何创建异常
How to create exceptions?
因此,我即将完成一项处理异常的作业,并在我当前的通讯录程序中使用它们,而大多数家庭作业都围绕着这个程序。我决定绕过例外和整个尝试-捕捉的事情,并使用课堂设计,这是我在几周后最终必须为我的作业做的。我有一些工作代码可以很好地检查异常,但我想知道的是,是否有一种方法可以标准化我的错误消息函数(即我的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((函数返回一个值,这个值取决于调用的错误类型。例如,如果我调用一个错误,它看起来像一个顶部的数字(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
}
这显然是行不通的,但有没有办法做到这一点,这样我就不会为每个错误消息编写不同的函数?
您的代码包含许多误解。简短的回答是肯定的,您可以更改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;
首先,从exception
派生的runtime_error
是要派生的建议异常类。这是在stdexcept标头中声明的。您只需要使用将在what()
方法中返回的消息来初始化它的构造函数。
其次,你应该适当地命名你的类。我知道这只是一个测试,但一个描述性的名称总是有助于阅读和理解您的代码。
正如您所看到的,我已经更改了构造函数,以便接受引发异常的要除法的数字。你在异常中进行了测试。。。嗯,我尊重这一点,但它是一个可以从外部调用的静态函数。
最后,what()
方法。既然我们正在划分两个数字,那么最好显示引发异常的两个数字。实现这一目标的唯一途径是使用鸵鸟流。在这里,我们将其设为静态,这样就不存在返回指向堆栈对象的指针的问题(即,将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;
}
正如您所看到的,在出现错误的情况下,将执行直到输入"正确"的除法。
希望这能有所帮助。
您可以为长度错误创建自己的异常类,如
class MyException : public std::length_error{
public:
MyException(const int &n):std::length_error(to_string(n)){}
};
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();
}
您应该考虑类的层次结构
当试图仅使用异常来传输字符串时,其原因可能并不明显,但使用异常的实际意图应该是一种高级处理异常情况的机制。当调用堆栈从"throw"移动到相应的"catch"时,许多事情都是在C++运行时环境的框架下完成的。
类的一个例子可能是:
class CalculationError : public std::runtime_error {
public:
CalculationError(const char * message)
:runtime_error(message)
{
}
};
class ZeroDeviderError : public CalculationError {
public:
ZeroDeviderError(int numerator, const char * message)
: CalculationError(message)
, numerator (numerator)
{
}
int GetNumerator() const { return numerator; }
private:
const int numerator;
};
- 为错误提供不同的类,使开发人员有机会以特定的方式处理不同的错误(而不仅仅是显示错误消息(
- 为错误类型提供基类,使开发人员能够更加灵活——根据需要进行具体化
在某些情况下,他们可能希望是特定的
} catch (const ZeroDividerError & ex) {
// ...
}
在其他方面,而不是
} catch (const CalculationError & ex) {
// ...
}
一些附加细节:
- 在以这种方式抛出之前,不应该创建异常的对象。不管你的意图如何,它都是无用的——不管怎样,你正在catch部分中处理对象的副本(不要被通过引用的访问所混淆——抛出时会创建异常对象的另一个实例(
- 除非您真的需要一个非常量对象,否则使用const引用将是一个很好的样式
catch (const testException &te)
此外,请注意,用于异常的类型(类(不允许从其复制构造函数中抛出异常,因为如果试图通过值捕获初始异常,则可以调用复制构造函数(在编译器未消除的情况下(,并且此附加异常将在捕获初始异常之前中断初始异常处理,这导致调用std::terminate。由于在某些情况下,C++11编译器在捕获时可以消除复制,但省略并不总是合理的,如果合理的话,这只是许可而不是义务(请参阅https://en.cppreference.com/w/cpp/language/copy_elision详细信息;在C++11之前,该语言的标准并没有规范这一问题(。
此外,您应该避免从构造函数中抛出异常(将其称为额外的(,并移动用于异常的类型(类(的构造函数(将称为初始构造函数(,因为在将类型的对象作为初始异常抛出时可以调用构造函数和移动构造函数,那么抛出额外的异常将阻止创建初始异常对象,而初始异常对象将丢失。以及来自复制构造函数的额外异常,当抛出初始异常时,也会导致同样的情况。
- 创建具有 new in 函数和"this is nullptr"异常的对象
- 您应该在什么时候创建自己的异常类型
- 我正在尝试创建一个使用 c++ 中的参数包构造函数的异常类
- C++ 异常从字符串转换为c_str会创建垃圾字符
- 如果一个对象是在本地创建的,并在C++中作为异常抛出,那么本地对象如何在其范围之外有效,即在 catch 块中?
- Windows错误:异常:使用从C++到Python的ctypes创建DLL时出现访问冲突或Windows错误193
- 如何防止构造函数在引发异常时创建对象
- 挂钩创建文件抛出异常:读取访问冲突
- 为什么要创建自己的自定义异常类
- 尝试创建 vkCreateImageView 会导致异常
- 创建自己的异常(2种方法)C
- 无法创建两个从 std::logic_error 继承的自定义异常类
- 在C++中创建自定义异常
- 异常表达式创建的异常对象的类型
- 在QT中创建我自己的异常并在函数中抛出异常
- 是否可以对构造函数抛出异常的对象进行异常处理,该对象的异常处理接近其基于堆栈的代码创建
- C++通过继承 std::exception 来创建新的异常
- 创建继承自C++中io类的异常类
- 创建CFrameWnd会给出第一次机会的异常——为什么
- Boost::interprocess_library_error异常创建shared_memory_object