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(char, int);
/*不抛出任何异常*/
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异常