C++中的异常处理

时间:2020-02-23 14:29:56  来源:igfitidea点击:

在本文中,我们将介绍在C++中执行异常处理。

异常处理是一个重要的主题,尤其是在面向对象编程的情况下。
C++与Java类似,因为它可以引发和捕获异常。

我们在代码的单独部分中说明了处理异常的方法,因为它们使事情变得模块化,并使程序员更轻松。
如果检测到任何其他错误,则仅必须修改异常处理代码。

我们必须将任何引发异常的代码封装在try块下。
因此,异常处理的三个主要部分是:

  • 尝试块

  • 使用可选对象以及throw关键字引发异常。

  • 一个catch块,用于处理异常。
    该块通常紧接在" try"块之后。
    引发适当的异常后,程序将立即跳至本节。

该计划的总体结构为:

try {
  ...
  throw exception;
}

catch(object) {
  ...
}

让我们在以下部分中对此进行更详细的研究。

在C++中引发异常

用C++术语来说,我们将引发异常称为抛出异常。

这是通过throw关键字完成的。
这可以采用任何对象(或者原始类型)并将其传递到异常处理代码中。

抛出对象的语法是:

//Must enclose the code in a try block
try {
  int x = 0;
  call_function(x);
  if (x < 0) {
      //Throw the object, if something unwanted happened
      throw x;
  }
}

现在,由于没有catch块,程序显然还没有完成。

现在就写吧!

捕捉异常

我们提到程序会从" try"块跳转到合适的" catch"块(如果有)。

catch块具有以下结构:

catch(int x) {
  std::cout << "Exception thrown. Object " << x << " caught.\n"; 
}

每当我们的function_call(x)使x变为负数时,它将抛出异常,并显示一条消息。

现在让我们看一下完整的程序。

#include <iostream>

void function_call(int& x) {
  //Do some work, and modify the value of x, so we're passing by reference
  if (x == 0) {
      //Set it to some negative value. We'll use this to throw an exception
      x = -10;
      //Throw an exception, and pass the object x
      throw x;
  }
  else {
      x = 100;
  }
}

int main() {
  int x = 0;
  try {
      function_call(x);
      std::cout << "If this line executes, then x != 0\n";
  }
  catch(int x) {
      std::cout << "Exception thrown. Object " << x << " caught.\n"; 
  }
  return 0;
}

由于最初将x设置为0,我们将抛出一个异常,并打印修改后的x值(设置为-10),表明它已经执行了catch块。

另外,请注意,由于程序直接跳转到" catch",因此" function_call(x)"语句正下方的行永远不会执行。

输出

Exception thrown. Object -10 caught.

捕获所有类型的异常

如果您希望代码处理所有任意种类的异常该怎么办? C++引入了一项功能,该功能允许您使用特殊的匹配对象(...)来执行此操作。

为了捕获各种异常,我们可以编写catch(...)来捕获我们抛出的每个对象。
当我们无法与其他任何异常匹配时,这称为默认异常,它是后备选项。

为了说明这一点,这是一个示例。

#include <iostream>
#include <string>

void function_call(int& x) {
  //Do some work, and modify the value of x, so we're passing by reference
  if (x == 0) {
      //Throw an exception, and pass on the object x
      throw x;
  }
  else if (x < 0) {
      //If x is negative, throw a random string
      throw std::string("Default String");
  }
  else {
      x = 100;
  }

}

int main() {
  for (int i=0; i>=-3; i--) {
      try {
          function_call(i);
          std::cout << "If this line executes, then x != 0\n";
      }
      catch(int x) {
          std::cout << "Exception thrown. Currently, x = " << x << " caught.\n";
      }
      catch(...) {
          std::cout << "Default exception. Currently, i = " << i << " caught.\n";
      }
  }
  return 0;
}

输出

Exception thrown. Currently, x = 0 caught.
Default exception. Currently, i = -1 caught.
Default exception. Currently, i = -2 caught.
Default exception. Currently, i = -3 caught.

如您所见,最初,当i = 0时,它会转到第一个catch块,因为我们返回一个整数。

但是,此后,由于我们返回字符串" Default String",因此它与第一个块不匹配,并且转到了默认块。

不幸的是,C++没有提供在默认情况下传递对象的方法,因此我们无法获得"默认字符串"字符串。
这表明我们只能将其用于为代码添加功能,以解决未知错误。

传递指针和对异常的引用

由于我们正在使用C++,因此这也是可能的!

一般规则是处理异常处理代码中与指针相关的所有释放。

try {
  call_function(base_ptr);
}
//Catch the Custom Exception Pointer using our Exception Class
catch(MyException* ex_obj) {
  delete ex_obj;
}

//Catch the reference to an Exception Class object
catch(MyException& ex_obj) {
  //Do stuff ...
}

让我们考虑下面的异常处理程序类" MyException"。

#include <iostream>
#include <string>

class MyException {
  public:
      int err; //Error Code
      std::string err_msg; //String containing Error Message
      MyException(int val = 0) {
          //Base Constructor
          err = val;
          switch(val) {
              case(-1): err_msg = "Case -1"; break;
              case(-2): err_msg = "Case -2"; break;
              case(-3): err_msg = "Case -3"; break;
              default: err_msg = "##代码##"; //No error message for other cases
          }
      }
      ~MyException() {
          std::cout &lt;&lt; "Destructor for Exception Class called\n";
      }
};

void function_call(int& x) {
  //Do some work, and modify the value of x, so we're passing by reference
  if (x &lt;= 0) {
      //Throw an exception, and pass on the object x
      MyException* ex = new MyException(x);
      throw ex; //Throw the exception pointer
  }
  else {
      x = 100;
  }

}

int main() {
  for (int i=0; i>=-3; i--) {
      try {
          function_call(i);
          std::cout &lt;&lt; "If this line executes, then x != 0\n";
      }
      catch(MyException* ex) {
          std::cout &lt;&lt; "Exception thrown. Error Message: " &lt;&lt; ex->err_msg &lt;&lt; "\n";
          //Remember to free the pointer to the Exception!!!
          delete ex;
      }
      catch(...) {
          std::cout &lt;&lt; "Default exception. Currently, i = " &lt;&lt; i &lt;&lt; " caught.\n";
      }
  }
  return 0;
}

我们不是直接传递整数,而是构造异常类,然后将指针传递给对象,该对象具有相关的错误代码和错误消息。

其中您可以看到使用Exception Class处理程序的强大功能!

我们不仅可以在构造函数中向其添加调试消息,而且在实际传递该消息时,我们仅移动了仅8个字节的指针!这是Java之类的其他语言无法轻易完成的。

确实,这就是我们的期望!仅针对值-1,-2和-3,我们指示构造函数设置错误消息。
对于其他值,它只是一个" NULL"字符串。

C++中异常处理的其他要点

  • 当使用trycatch块时,没有隐式类型转换发生。
    因此,当将" char"传递给异常处理程序时,不会将其转换为" int"。

  • 如果使用try块,则始终使用catch块。
    否则,如果引发异常,则将导致程序异常终止。

  • 模块化您的代码,以便您的核心程序逻辑与错误处理逻辑不同。
    对于异常,请尝试使用相关的异常处理程序类和多态性来处理错误。

  • 如果您使用原始指针,请务必记住在您的catch块中释放它们!如果发现原始点太麻烦,请改用智能指针!