1、异常是什么?
- 程序执行期间出现的错误或意外情况。
2、传统的异常处理方式
- 条件判断、分支处理 if
- 优点:实现简单直观
- 缺点:a、种类及情况太多,在大型系统中很难全部考虑到,导致程序崩溃,终止运行
b、某些异常情况的发生具有随机性,无法在程序中做出明确的处理,但是我们有时候又希望程序能够跳过异常部分错误运行。
c、我们希望可以传递异常信息,也就是发生异常时,可以把异常信息发送到其他地方进行处理。
3、C++中的异常机制
当程序中发生异常时,生成一个异常对象来记录异常信息,然后把该异常发送到异常处理程序。
抛出异常:可以通过throw关键字 throw new DivideZero()// 抛出一个除0 异常的临时对象。
捕获异常、处理异常:try{} catch(异常对象(*)) {}
4、异常机制的使用:
想要在C++中使用异常,首先要自己定义异常,然后才能在特定情况下,通过throw 抛出异常。
(java 、python 用过了感觉很不方便呀)
我们可以定义一个空的异常类, 也可以在异常类中添加成员,以获取调用层级关系,建议的解决方法等。
5、异常的处理方法
可以在抛出异常的地方直接处理,在可以明确异常信息的情况下使用。通常就是传送到使用的函数中进行处理。
示例代码:
class DivideZero // 定义异常类,和普通类的定义一样,可以采用继承等结构
{
public:
voidshow()
{
printf("dividezero");
}
};
classA
{
public:
voidfun(int x, int y)
{
if(0 == x)
{
throw new DivideZero(); // 抛出异常
}
printf("%d\n",x+y);
}
};
int_tmain(int argc, _TCHAR* argv[])
{
Aa;
try
{
a.fun(0,1);
}
catch (DivideZero * e) // 捕获异常
{
printf("error");
e->show();
}
return0;
}
通过把异常传递给函数的调用者来处理异常。
classA
{
public:
voidfun(int x, int y)
{
try{
if(0 == x)
{
thrownew DivideZero();
}
}
catch(DivideZero * pe)
{
pe->show();
}
printf("%d\n",x+y);
}
};
在抛出异常的地方直接处理。
6、编译器是如何对异常进行处理的?
涉及到一些运行库的内容,目前还不是很清楚,现在只从反汇编的角度做以下初步的解释。
fun 函数中的throw 块
if(0 == x)
01102D83 cmp dword ptr [x],0 // 比较x 和0的值
01102D87 jne A::fun+9Eh(01102DFEh) // 不相等的时候直接跳过
{
thrownew DivideZero();
01102D89 mov dword ptr [ebp-0E0h],1
01102D93 mov eax,dword ptr [ebp-0E0h]
01102D99 push eax
01102D9A call operator new(0110141Fh) // 生成异常对象,指针在eax中
01102D9F add esp,4
01102DA2 mov dword ptr [ebp-0D4h],eax
01102DA8 cmp dword ptr [ebp-0D4h],0
01102DAF je A::fun+77h (01102DD7h)
01102DB1 mov ecx,dword ptr [ebp-0E0h]
01102DB7 push ecx
01102DB8 push 0
01102DBA mov edx,dword ptr [ebp-0D4h]
01102DC0 push edx
01102DC1 call _memset (01101514h)
01102DC6 add esp,0Ch
01102DC9 mov eax,dword ptr [ebp-0D4h]
01102DCF mov dword ptr [ebp-0F4h],eax
01102DD5 jmp A::fun+81h (01102DE1h)
01102DD7 mov dword ptr [ebp-0F4h],0
01102DE1 mov ecx,dword ptr [ebp-0F4h]
01102DE7 mov dword ptr [ebp-0ECh],ecx
01102DED push 110F824h
01102DF2 lea edx,[ebp-0ECh]
01102DF8 push edx
01102DF9 call __CxxThrowException@8(0110128Fh) //调用运行库中的中断抛出函数
}
<<throw.cpp>>
附件中为ThrowException的解释。抛出异常由运行时库来进行,设计上类似于CPU的中断,都有一个中断码,在catch中由中断码来判断异常类型。
try
{
01109664 mov dword ptr [ebp-4],0
a.fun(0,1);
0110966B push 1
0110966D push 0
0110966F lea ecx,[a]
01109672 call A::fun (0110150Ah)
}
01109677 jmp wmain+7Eh (0110969Eh) // 1
catch(DivideZero * e)
{
printf("error");
01109679 mov esi,esp
0110967B push 110D9B8h
01109680 call dword ptr ds:[11111C0h]
01109686 add esp,4
01109689 cmp esi,esp
0110968B call __RTC_CheckEsp (01101366h)
e->show();
01109690 mov ecx,dword ptr [ebp-28h]
01109693 call DivideZero::show (0110150Fh)
}
01109698 mov eax,11096A7h
0110969D ret
0110969E mov dword ptr [ebp-4],0FFFFFFFFh
011096A5 jmp $LN7+7h (011096AEh)
$LN7:
011096A7 mov dword ptr [ebp-4],0FFFFFFFFh
在没有异常情况发生时, try的末尾会像if 一样添加一条跳转语句,跳转到catch 后面执行,如果发生异常,则根据运行库的异常初步处理结果中的Exception number 来判断进入哪一个catch 语句中执行。