C++ 异常的详细介绍
C++异常的详解
程序有时会遇到运行阶段错误,导致程序无法正常执行下去。c++异常为处理这种情况提供了一种功能强大的而灵活的工具。异常是相对比较新的C++功能,有些老编译器可能没有实现。另外,有些编译器默认关闭这种特性,我们可能需要使用编译器选项来启用它。
一、异常机制的使用
异常提供了将控制程序的一个部分传递到另一部分的途径。对异常的处理有3个组成部分:
引发异常
使用处理程序捕获异常
使用try块
示例代码:
#include"stdafx.h" #includedoublehmean(doublea,doubleb); intmain() { doublex,y,z; std::cout<<"Entertwonumbers:"; while(std::cin>>x>>y){ try { z=hmean(x,y); } catch(constchar*s){ std::cout< :"; } std::cout<<"Bye!\n"; system("pause"); return0; } doublehmean(doublea,doubleb){ if(a==-b){ throw"badhmean()argumentsa=-bnotallowed"; } return2.0*a*b/(a+b); } Entertwonumbers:36 Harmonicmeanof3and6is4 Enternextsetofnumbers:10-10 badhmean()argumentsa=-bnotallowed Enteranewpairofnumbers:q Bye! 请按任意键继续...
程序说明:
try块:
try
{
z=hmean(x,y);
}
引发异常的代码:
if(a==-b){
throw"badhmean()argumentsa=-bnotallowed";
}
执行throw语句类似于执行返回语句,因为他也将终止函数的执行;但throw不是讲控制权返回给调用程序,而是导致程序沿函数调用序列后退,知道找到包含try块的函数。
处理程序(或catch块):
catch(constchar*s){
std::cout<
二、将对象用作异常类型
通常,引发异常的函数将传递一个对象。这样做的重要优点之一是,可以使用不同的异常类型来区分不同的函数在不同情况下引发的异常。另外,对象可以携带信息,程序员可以根据这些信息来确定引发异常的原因。同时,catch块可以根据这些信息来决定采取什么样的措施。
示例:
exc_mean.h
#include"stdafx.h"
#include
classbad_hmean
{
private:
doublev1;
doublev2;
public:
bad_hmean(doublea=0,doubleb=0):v1(a),v2(b){}
voidmesg();
};
inlinevoidbad_hmean::mesg()
{
std::cout<<"hmean("<=0\n";
}
测试代码:
#include"stdafx.h"
#include
#include
#include"exc_mean.h"
doublehmean(doublea,doubleb);
doublegmean(doublea,doubleb);
intmain()
{
usingstd::cout;
usingstd::cin;
usingstd::endl;
doublex,y,z;
1>>2;
cout<<"Entertwonumbers";
while(cin>>x>>y)
{
try
{
z=hmean(x,y);
cout<<"Harmonicmeanof"<:";
}
catch(bad_hmean&bg)
{
bg.mesg();
cout<<"Tryagain.\n";
continue;
}
catch(bad_gmean&hg)
{
cout<:5-5
hmean(5,-5);invalidargumnents:a=-b
Tryagain.
5-2
Harmonicmeanof5and-2is-6.66667
gmean()argumentsshoudbe>=0
Valueused:5,-2
Sorry,youdon'tgettoplayanymore.
Bye!
请按任意键继续...
三、异常规范
异常规范是C++98的一项功能,但c++11将其摒弃了。这意味着c++11仍然处于标准之中,但以后可能会从标准中剔除,因此不建议使用它。
异常规范示例:
doubleharm(doublea)throw(bad_thing);//可能会抛出bad_thing异常
doublemarm(double)throw();//不抛出异常
异常规范的作用:
1、告诉用户可能需要使用try块,然而这项功能也可使用注释轻松完成。
2、让编译器添加执行运行阶段检查代码,检查是否违反了异常规范,然而这很难检查,例如marm可能不会引发异常,但它可能调用一个函数,而这个函数调用另一个函数引发了异常
总之最好不要使用这项功能,c++11也建议忽略异常规范
然而c++11确实支持一种特殊的异常规范,可使用关键字noexcept
例如
doublemarm()noexcept;
四、栈解退
假设函数由于异常(而不是由于返回)而终止,则程序也将释放栈中的内存,但不会师范栈的第一个返回地址后停止,而是继续释放栈,直到找到一个位于Try块的返回地址。随后,控制权将转到块尾的异常处理程序,而不是函数调用后面的第一条语句。这个过程叫做栈解退。
五、exception类
较新的C++编译器将异常合并到语言中,例如,为支持该语言,exception头文件(以前为exception.h或except.h)定义了exception类,c++可以把它用作其他异常类的基类。
头文件exceptionhe和stdexcept定义了一些常用的异常类
有:logic_error、runtime_error、domain_error等
六、意外异常与未捕获异常处理
异常引发后,在两种情况下,会导致问题。首先,如果它是在带异常规范的函数中引发的,则必须与规范列表的某种异常匹配(在继承层次机构中,类类型与这个类与其派生的对象匹配),否则成为意外异常。在默认情况下,这将导致程序异常终止(虽然C++11摒弃了异常规范,但仍支持它,且有些现有代码使用了它)如果异常不是在函数中引发的(或者函数没有异常规范),则必须捕获它,如果没有捕获(在没有try块或没有匹配的catch块时,将出现这种情况),则异常被称未捕获异常。这将导致程序异常终止。然而可以修改程序对意外异常和为捕获异常的反应。
未捕获异常:
未捕获异常不会导致程序立即异常中终止,相反,程序将首先调用函数terminate()。在默认情况下terminate()调用abort()函数。可以指定terminate()应调用的函数(而不是abort())来修改terminate()的这种行为。为此,可调用set_terminate()函数。set_terminate()和terminate()都是在头文件exception中声明的:
typedefvoid(*terminate_handle)();
terminate_handleset_terminate(terminate_handlef)throw();//c++98
terminate_handleset_terinate(terminate_handlef)noexcept;//c++11
voidteminate();//c++98
voidteminate()noexcept;//c++11
示例:
voidmyQuit()
{
std::cout<<"Terminatingduetouncaughtexception\n";
system("pause");
}
在程序开始时执行:
set_terminate(myQuit);
意外异常
如果发生意外异常,程序将调用unexcepted()函数,这个函数将调用teminate(),后者默认滴啊用abort()。和set_terminate()函数一样,也有一个可用于修改unexcepted()的行为的set_unexpeceted()函数。这些函数也是在头文件exception中声明的:
typedefvoid(*unexpected_handle)();
unexpected_handleset_unexpected(unexpected_handlef)throw();//c++98
unexpected_handleset_unexpected(unexpected_handlef)noexpect;//c++11
voidunexpected();c++98
voidunexpected()noexcept;//c+0x
使用如下:
voidmyUnexpected(){
throwstd::bad_exception();//orjustthrow;
}
在程序开始时:
set_unexpected(myUnexpected);
我在vs2015下测试,并未实现这种功能,必须显示调用terminate()和unexpected();
如有疑问请留言或者到本站社区交流讨论,感谢阅读,希望能帮助到大家,谢谢大家对本站的支持!