1

main 函数中抛出异常

问题: 如果在main函数中抛出异常会发生什么?

  • 如果异常不处理,最后会传到哪里?

clipboard.png

编程实验: 异常的最终处理

下面的代码会输出什么呢?

#include <iostream>

using namespace std;

class Test
{

public:
    Test()
    {
        cout << "Test()" << endl;
    }
    
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    static Test t;
    
    throw 1;

    return 0;
}
输出:【g++】
Test()
terminate called after throwing an instance of 'int'
已放弃

输出:【vc++2010】
Test()
...
应用程序自动退出...

思考:
不同的编译器为什么有不同的行为,g++ 中的提示信息是在哪里打印的呢?

  • 如果异常无法被处理, terminate() 结束函数会被自动调用
  • 默认情况下, terminate() 调用库函数 abort() 终止程序
  • abort() 函数使得程序执行异常而立即退出
  • C++ 支持替换默认的 terminate() 函数实现

  • terminate() 函数的替换

    • 自定义一个无返回值无参数的函数

      • 不能抛出任何异常
      • 必须以某种方式结束当前程序
    • 调用 set_terminate() 设置自定义的结束函数

      • 参数类型为 void(*)()
      • 返回值为默认的 terminate() 函数入口地址

编程实验: 自定义结束函数

#include <iostream>
#include <cstdlib>
#include <exception>

using namespace std;

void my_terminate()
{
    cout << "void my_terminate()" << endl;
    exit(1);  // 注意这里!
};

class Test
{

public:
    Test()
    {
        cout << "Test()" << endl;
    }
    
    ~Test()
    {
        cout << "~Test()" << endl;
    }
};

int main()
{
    set_terminate(my_terminate);

    static Test t;
    
    throw 1;

    return 0;
}
输出:[g++]
Test()
void my_terminate()
~Test()

输出:[vc++2010]
Test()
void my_terminate()
~Test()

分析: 析构函数为什么会被调用呢?

  • abort() : 使应用程序异常停止,立即退出
  • exit(x) : 确保所有的全局对象和静态局部对象正常析构后,程序退出

当 my_terminate() 为 abort() 时:

输出:[g++]
Test()
void my_terminate()
已放弃

输出:[vc++2010]
Test()
void my_terminate()
...
应用程序自动退出...

析构函数中抛出异常

面试题: 如果析构函数中抛出异常会发生什么情况呢?

编程实验: 析构函数抛出异常

#include <iostream>
#include <cstdlib>
#include <exception>

using namespace std;

void my_terminate()
{
    cout << "void my_terminate()" << endl;
    exit(1);
};

class Test
{

public:
    Test()
    {
        cout << "Test()" << endl;
    }
    
    ~Test()
    {
        cout << "~Test()" << endl;
        
        throw 2;
    }
};

int main()
{
    set_terminate(my_terminate);

    static Test t;
    
    throw 1;

    return 0;
}
输出:
Test()
void my_terminate()
~Test()
void my_terminate()

分析:
第一次 "void my_terminate()" : main() 函数中抛出的异常
第二次 "void my_terminate()" : exit(1) 促使所有全局对象与静态局部对象销毁,析构函数被调用,抛出异常
  • 析构函数用来释放资源,如果在析构函数中抛出异常,资源可能得不到正确释放。
  • 有可能导致全局的 terminate 函数被重读调用,重复调用是未定义行为,甚至可能影响系统安全。

my_terminate 进行应用程序级别的资源释放。而重复释放的行为是未定义,因此默认的 terminate 函数中使用 abort() 异常退出程序来避免构造函数被执行(防止析构函数中再次抛出异常)

设计原则 : 不允许在析构函数中抛出异常

小结

  • 如果异常没有被处理,最后 terminate() 结束整个程序
  • terminagte() 是整个程序释放资源的最后机会
  • 结束函数可以自定义,但不能继续抛出异常
  • 析构函数中不能抛出异常,可能导致 terminate() 多次调用

以上内容参考狄泰软件学院系列课程,请大家保护原创!


TianSong
734 声望138 粉丝

阿里山神木的种子在3000年前已经埋下,今天不过是看到当年注定的结果,为了未来的自己,今天就埋下一颗好种子吧