如何创建异常

How to create exceptions?

本文关键字:异常 创建 何创建      更新时间:2023-10-16

因此,我即将完成一项处理异常的作业,并在我当前的通讯录程序中使用它们,而大多数家庭作业都围绕着这个程序。我决定绕过例外和整个尝试-捕捉的事情,并使用课堂设计,这是我在几周后最终必须为我的作业做的。我有一些工作代码可以很好地检查异常,但我想知道的是,是否有一种方法可以标准化我的错误消息函数(即我的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之前,该语言的标准并没有规范这一问题(。

此外,您应该避免从构造函数中抛出异常(将其称为额外的(,并移动用于异常的类型(类(的构造函数(将称为初始构造函数(,因为在将类型的对象作为初始异常抛出时可以调用构造函数和移动构造函数,那么抛出额外的异常将阻止创建初始异常对象,而初始异常对象将丢失。以及来自复制构造函数的额外异常,当抛出初始异常时,也会导致同样的情况。