📜  C++中的异常处理

📅  最后修改于: 2021-05-25 20:54:38             🧑  作者: Mango

与C相比,C++的优点之一是异常处理。异常是程序在执行过程中遇到的运行时异常或异常情况。有两种类型的异常:a)同步,b)异步(例如:超出程序的控制范围,光盘故障等)。为此,C++提供了以下专用关键字。
try :表示可能引发异常的代码块。
catch :表示抛出特定异常时执行的代码块。
throw :用于引发异常。也用于列出函数引发但无法自行处理的异常。

为什么要进行异常处理?
以下是异常处理相对于传统错误处理的主要优点。

1)错误处理代码与普通代码的分离:在传统的错误处理代码中,总是存在其他条件来处理错误。这些条件和处理错误的代码会与正常流程混在一起。这使代码的可读性和可维护性降低。使用try catch块,用于错误处理的代码将与正常流程分开。

2)函数/方法可以处理他们选择的任何异常:函数可以引发许多异常,但是可以选择处理其中的一些异常。引发但未被捕获的其他异常可以由调用方处理。如果呼叫者选择不捕获它们,则异常由呼叫者的呼叫者处理。
在C++中,函数可以使用throw关键字指定其引发的异常。此函数的调用者必须以某种方式处理异常(通过再次指定它或捕获它)

3)错误类型分组:在C++中,基本类型和对象都可以作为异常抛出。我们可以创建异常对象的层次结构,在名称空间或类中对异常进行分组,并根据类型对它们进行分类。

C++中的异常处理

1)下面是一个简单的示例,以显示C++中的异常处理。程序的输出说明了try / catch块的执行流程。

CPP
#include 
using namespace std;
 
int main()
{
   int x = -1;
 
   // Some code
   cout << "Before try \n";
   try {
      cout << "Inside try \n";
      if (x < 0)
      {
         throw x;
         cout << "After throw (Never executed) \n";
      }
   }
   catch (int x ) {
      cout << "Exception Caught \n";
   }
 
   cout << "After catch (Will be executed) \n";
   return 0;
}


CPP
#include 
using namespace std;
 
int main()
{
    try  {
       throw 10;
    }
    catch (char *excp)  {
        cout << "Caught " << excp;
    }
    catch (...)  {
        cout << "Default Exception\n";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
int main()
{
    try  {
       throw 'a';
    }
    catch (int x)  {
        cout << "Caught " << x;
    }
    catch (...)  {
        cout << "Default Exception\n";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
int main()
{
    try  {
       throw 'a';
    }
    catch (int x)  {
        cout << "Caught ";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
// This function signature is fine by the compiler, but not recommended.
// Ideally, the function should specify all uncaught exceptions and function
// signature should be "void fun(int *ptr, int x) throw (int *, int)"
void fun(int *ptr, int x)
{
    if (ptr == NULL)
        throw ptr;
    if (x == 0)
        throw x;
    /* Some functionality */
}
 
int main()
{
    try {
       fun(NULL, 0);
    }
    catch(...) {
        cout << "Caught exception from fun()";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
// Here we specify the exceptions that this function
// throws.
void fun(int *ptr, int x) throw (int *, int) // Dynamic Exception specification
{
    if (ptr == NULL)
        throw ptr;
    if (x == 0)
        throw x;
    /* Some functionality */
}
 
int main()
{
    try {
       fun(NULL, 0);
    }
    catch(...) {
        cout << "Caught exception from fun()";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
int main()
{
    try {
        try {
            throw 20;
        }
        catch (int n) {
            cout << "Handle Partially ";
            throw; // Re-throwing an exception
        }
    }
    catch (int n) {
        cout << "Handle remaining ";
    }
    return 0;
}


CPP
#include 
using namespace std;
 
class Test {
public:
    Test() { cout << "Constructor of Test " << endl; }
    ~Test() { cout << "Destructor of Test " << endl; }
};
 
int main()
{
    try {
        Test t1;
        throw 10;
    }
    catch (int i) {
        cout << "Caught " << i << endl;
    }
}


输出:

Before try
Inside try
Exception Caught
After catch (Will be executed)

2)有一个特殊的catch块,称为“ catch all” catch(…),可用于捕获所有类型的异常。例如,在下面的程序中,将引发一个int作为异常,但是没有用于int的catch块,因此将执行catch(…)块。

CPP

#include 
using namespace std;
 
int main()
{
    try  {
       throw 10;
    }
    catch (char *excp)  {
        cout << "Caught " << excp;
    }
    catch (...)  {
        cout << "Default Exception\n";
    }
    return 0;
}

输出:

Default Exception

3)原始类型不会发生隐式类型转换。例如,在下面的程序中,“ a”未隐式转换为int

CPP

#include 
using namespace std;
 
int main()
{
    try  {
       throw 'a';
    }
    catch (int x)  {
        cout << "Caught " << x;
    }
    catch (...)  {
        cout << "Default Exception\n";
    }
    return 0;
}

输出:

Default Exception

4)如果引发异常且未在任何地方捕获异常,则程序异常终止。例如,在下面的程序中,抛出了一个char,但是没有catch块来捕获char。

CPP

#include 
using namespace std;
 
int main()
{
    try  {
       throw 'a';
    }
    catch (int x)  {
        cout << "Caught ";
    }
    return 0;
}

输出:

terminate called after throwing an instance of 'char'

This application has requested the Runtime to terminate it in an 
unusual way. Please contact the application's support team for 
more information.

我们可以通过编写自己的意外函数来更改此异常终止行为。
5)应该在派生类异常之前捕获派生类异常。有关更多详细信息,请参见此内容。
6)与Java一样,C++库具有一个标准异常类,该类是所有标准异常的基类。标准库的组件抛出的所有对象均派生自此类。因此,通过捕获此类型可以捕获所有标准异常
7)与Java不同,在C++中,所有异常均未选中。编译器不会检查是否捕获到异常(有关详细信息,请参见此内容)。例如,在C++中,没有必要在函数声明中指定所有未捕获的异常。尽管建议这样做。例如,以下程序可以正常编译,但理想情况下fun()的签名应列出未检查的异常。

CPP

#include 
using namespace std;
 
// This function signature is fine by the compiler, but not recommended.
// Ideally, the function should specify all uncaught exceptions and function
// signature should be "void fun(int *ptr, int x) throw (int *, int)"
void fun(int *ptr, int x)
{
    if (ptr == NULL)
        throw ptr;
    if (x == 0)
        throw x;
    /* Some functionality */
}
 
int main()
{
    try {
       fun(NULL, 0);
    }
    catch(...) {
        cout << "Caught exception from fun()";
    }
    return 0;
}

输出:

Caught exception from fun()

编写上述代码的更好方法

CPP

#include 
using namespace std;
 
// Here we specify the exceptions that this function
// throws.
void fun(int *ptr, int x) throw (int *, int) // Dynamic Exception specification
{
    if (ptr == NULL)
        throw ptr;
    if (x == 0)
        throw x;
    /* Some functionality */
}
 
int main()
{
    try {
       fun(NULL, 0);
    }
    catch(...) {
        cout << "Caught exception from fun()";
    }
    return 0;
}

(注意:动态异常规范的使用在C++ 11之后已被弃用,原因之一可能是因为它可以随机中止程序。当您抛出动态变量中未提及的另一种类型的异常时,可能会发生这种情况。异常说明,您的程序将自行中止,因为在这种情况下,程序将调用(间接地)终止(),并且默认情况下将调用中止()。

输出:

Caught exception from fun()

8)在C++中,try-catch块可以嵌套。同样,可以使用“ throw; throw; throw”来重新抛出异常。 ”

CPP

#include 
using namespace std;
 
int main()
{
    try {
        try {
            throw 20;
        }
        catch (int n) {
            cout << "Handle Partially ";
            throw; // Re-throwing an exception
        }
    }
    catch (int n) {
        cout << "Handle remaining ";
    }
    return 0;
}

输出:

Handle Partially Handle remaining

一个函数也可以使用相同的“ throw; throw”来重新抛出一个函数。 ”。一个函数可以处理一部分,并且可以要求调用方处理剩余部分。
9)引发异常时,在将控件转移到catch块之前,将破坏在try块内部创建的所有对象。

CPP

#include 
using namespace std;
 
class Test {
public:
    Test() { cout << "Constructor of Test " << endl; }
    ~Test() { cout << "Destructor of Test " << endl; }
};
 
int main()
{
    try {
        Test t1;
        throw 10;
    }
    catch (int i) {
        cout << "Caught " << i << endl;
    }
}

输出:

Constructor of Test
Destructor of Test
Caught 10
要从最佳影片策划和实践问题去学习,检查了C++基础课程为基础,以先进的C++和C++ STL课程基础加上STL。要完成从学习语言到DS Algo等的更多准备工作,请参阅“完整面试准备课程”