如何创建例外?

时间:2013-04-24 03:18:56

标签: c++ exception custom-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
  }

这显然不起作用,但是有没有办法让它如此我不是为每个错误信息编写不同的函数?

4 个答案:

答案 0 :(得分:36)

您的代码包含许多误解。简短的回答是肯定的,您可以更改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;

}

如您所见,如果出现错误,则执行此操作直到输入“正确”除法。

希望这有帮助。

答案 1 :(得分:3)

您可以像这样

创建自己的长度错误异常类
class MyException : public std::length_error{
public:
  MyException(const int &n):std::length_error(to_string(n)){}
};

答案 2 :(得分: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&amp; e。

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

答案 3 :(得分:1)

您应该考虑类的层次结构。

在尝试仅仅为了传输字符串时使用异常时,它的原因可能并不明显,但使用异常的实际意图应该是一种高级处理异常情况的机制。很多事情都是在C ++运行时环境的基础上完成的,而调用栈是从&#39; throw&#39;对应&#39; catch&#39;。

这些类的一个例子可能是:

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),除非你真的需要一个非常量对象。