C++深度解析教程笔记16-外传:异常处理深度解析, 函数的异常规格说明 , 动态内存申请的结果


本文学习自狄泰软件学院 唐佐林老师的 C++深度解析教程,图片全部来源于课程PPT,仅用于个人学习记录

外传篇 1 - 异常处理深度解析

问题
如果在main函数中抛出异常会发生什么?
在这里插入图片描述
在这里插入图片描述

实验

vs2010编译器

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>cd
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC

C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC>E:

E:\>cd test

E:\test>cd ex

E:\test\ex>cl E1-1.cpp
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.40219.01 版
版权所有(C) Microsoft Corporation。保留所有权利。

E1-1.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:E1-1.exe
E1-1.obj
#include <iostream>

using namespace std;

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


int main()
{
    static Test t;
    
    throw 1;
	
    return 0;
}
//Test()
//terminate called after throwing an instance of 'int'

异常处理深度解析
如果异常无法被处理,terminate()结束函数会被自动调用
默认情况下,terminate()调用库函数abort()终止程序
aborto函数使得程序执行异常而立即退出
■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);//会调用析构函数
abort();//不会调用析构函数
}

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


int main()
{
    set_terminate(my_terminate);//set
    
    static Test t;
    
    throw 1;
	
    return 0;
}

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

实验-析构函数抛出异常

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

using namespace std;

void my_terminate()//被调用2此,不可取
{
    cout << "void my_terminate()" << endl;
    exit(1);
    
}

class Test 
{
public:
    Test() 
    {
        cout << "Test()"; 
        cout << endl;
    }
	
    ~Test() 
    {
        cout << "~Test()"; 
        cout << endl;
        
        throw 2;//异常warning: throw will always call terminate() [-Wterminate]
    }
};


int main()
{
    set_terminate(my_terminate);
    
    static Test t;
    
    throw 1;
	
    return 0;
}

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

外传篇 2 - 函数的异常规格说明

C++提供语法用于声明函数所抛出的异常
异常声明作为函数声明的修饰符,写在参数列表后面

/*可能抛出任何异常*/
void func1();
/* 只能抛出的异常类型:char 和 int */ 
void func2() throw(charint);
/*不抛出任何异常*/
void func3() throw();

问题
如何判断一个函数是否会抛出异常
以及抛出哪些异常?

函数的异常规格说明
■异常规格说明的意义
一提示函数调用者必须做好异常处理的准备
一提示函数的维护者不要抛出其它异常
一异常规格说明是函数接口的一部分

问题
如果抛出的异常不在声明列表中
会发生什么?

在这里插入图片描述

实验

#include <iostream>

using namespace std;

void func() throw(int)//异常声明,与函数内部不一致void func() throw(int)
{
    cout << "func()";
    cout << endl;
    
    throw 'c';
}



int main()
{
    try 
    {
        func();
    } 
    catch(int) 
    {
        cout << "catch(int)";
        cout << endl;
    } 
    catch(char) 
    {
        cout << "catch(char)";
        cout << endl;
    }

    return 0;
}
/*vs2010运行结果:
E:\test>cd ex

E:\test\ex>cl E2-1.cpp
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.40219.01 版
版权所有(C) Microsoft Corporation。保留所有权利。

E2-1.cpp
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:E2-1.exe
E2-1.obj

E:\test\ex>E2-1.exe
func()
catch(char)

g++运行结果:
E:\test\ex>g++ E2-1.cpp
E2-1.cpp:5:13: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
 void func() throw(int)//
             ^~~~~

E:\test\ex>a
func()
terminate called after throwing an instance of 'char'

*/

函数异常规格说明
函数抛出的异常不在规格说明中,全局unexpectedo被调用
默认的 unexpectedO 函数会调用全局的 terminateO 函数
可以自定义函数替换默认的unexpectedO函数实现
注意:不是所有的C++编译器都支持这个标准行为,

unexpectedo函数的替换
一自定义一个无返回值无参数的函数
·能够再次抛出异常
√当异常符合触发函数的异常规格说明时,恢复程序执行
否则,调用全局terminateO函数结束程序
一 调用 set_unexpected ○设置自定义的异常函数
·参数类型为void(*)()
·返回值为默认的unexpected()函数入口地址
在这里插入图片描述

实验-自定义unexpected函数

#include <iostream>
#include <cstdlib>
#include <exception>//头文件

using namespace std;

void my_unexpected()//自定义unexpected函数
{
    cout << "void my_unexpected()" << endl;
    //exit(1);//case1
     //throw 1;//case2
   //throw 'a';//case3
}

void func() throw(int)
{
    cout << "func()";
    cout << endl;
    
    throw 'c';
}

int main()
{
    set_unexpected(my_unexpected);//设置
    
    try 
    {
        func();
    } 
    catch(int) 
    {
        cout << "catch(int)";
        cout << endl;
    } 
    catch(char) 
    {
        cout << "catch(char)";
        cout << endl;
    }

    return 0;
}
/*case1:
E:\test\ex>g++ E2-2.cpp
E2-2.cpp:14:13: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
 void func() throw(int)
             ^~~~~

E:\test\ex>a
func()
void my_unexpected()

E:\test\ex>cl E2-2.cpp
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.40219.01 版
版权所有(C) Microsoft Corporation。保留所有权利。

E2-2.cpp
E2-2.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:E2-2.exe
E2-2.obj

E:\test\ex>E2-2.exe
func()
catch(char)


case2:
E:\test\ex>g++ E2-2.cpp
E2-2.cpp:14:13: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
 void func() throw(int)
             ^~~~~

E:\test\ex>a
func()
void my_unexpected()
catch(int)    //当异常符合触发函数的异常规格说明时,恢复程序执行

E:\test\ex>cl E2-2.cpp
用于 80x86 的 Microsoft (R) 32 位 C/C++ 优化编译器 16.00.40219.01 版
版权所有(C) Microsoft Corporation。保留所有权利。

E2-2.cpp
E2-2.cpp : warning C4819: 该文件包含不能在当前代码页(936)中表示的字符。请将该文件保存为 Unicode 格式以防止数据丢失
C:\Program Files (x86)\Microsoft Visual Studio 10.0\VC\INCLUDE\xlocale(323) : warning C4530: 使用了 C++ 异常处理程序,但未启用展开语义。请指定 /EHsc
Microsoft (R) Incremental Linker Version 10.00.40219.01
Copyright (C) Microsoft Corporation.  All rights reserved.

/out:E2-2.exe
E2-2.obj

E:\test\ex>E2-2.exe
func()
catch(char)

case3:
E:\test\ex>g++ E2-2.cpp
E2-2.cpp:15:13: warning: dynamic exception specifications are deprecated in C++11 [-Wdeprecated]
 void func() throw(int)
             ^~~~~

E:\test\ex>a
func()
void my_unexpected()
terminate called after throwing an instance of 'char'
*/

小结
C++中的函数可以声明异常规格说明
异常规格说明可以看作接口的一部分
函数抛出的异常不在规格说明中,unexpected()被调用
unexpected() 中能够再次抛出异常
异常能够匹配,恢复程序的执行
否则,调用terminate()结束程序

外传篇 3 - 动态内存申请的结果

问题
动态内存申请一定成功吗?
在这里插入图片描述

问题
new 语句中的异常是怎么抛出来的?

必须知道的事实!
malloc 函数申请失败时返回 NULL值
一new关键字申请失败时(根据编译器的不同)
·返回NULL值
·抛出std::badalloc异常

new 关键字在C++ 规范中的标准行为
在堆空间电请足够大的内存
成功:
在获取的空间中调用构造函数创建对象
返回对象的地址
失败:
抛出std::badalloc异常

在这里插入图片描述
在这里插入图片描述
问题
如何跨编译器统一new 的行为
提高代码移植性?

在这里插入图片描述

实验-证明newhandler存在

在这里插入代码片

不同编译器都不同

实验结论
一不是所有的编译器都遵循C++的标准规范
一编译器可能重定义new的实现,并在实现中抛出badalloc异常
编译器的默认实现中,可能没有设置全局的newhandlerO函数
一对于移植性要求较高的代码,需要考虑new的具体细节

实验

在这里插入代码片

实验

在这里插入代码片

小结

不同的编译器在动态内存分配上的实现细节不同
malloc函数在内存申请失败时返回NULL值
new关键字在内存申请失败时
可能返回NULL值
可能抛出bad_alloc异常

  • 8
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值