由于Symbian OS所强调的是简洁的操作系统和客户代码,异常退出(Leave)是用于取代相对开销较大的C++异常处理,以节省其所增加之运行时的存储器开销,以及编译出的代码大小。
在编译基于Symbian OS的代码时,编译器禁止C++异常处理,所有使用try、catch或throw关键字的地方将被报错。
在异常退出时容易发生内存泄露。
1、异常退出函数的定义
异常退出函数中可能发生异常,函数名后加“L”。一般来说,除非需要将函数中所分配资源的指针或引用作为返回值,否则异常退出函数无返回值。
当发生异常退出时,抛出异常同时产生一个错误代码,沿栈传递至异常捕获模块将其“捕获”。异常退出通过TRAP宏将栈指针指向捕获异常的context,然后由捕获模块来跳转到特定的程序位置,并恢复寄存器的值。从而,整个过程相当于:代码执行到异常退出点停止,然后返回到异常被捕获的地方。
函数可能发生异常退出的情况:
(1)调用可能异常退出的代码而未在其周围使用异常捕获模块;
(2)调用一个会产生异常退出的系统函数;
(3)使用了以Eleave为参数的new操作符重载形式。例如:CClanger* clanger = new (ELeave) Cclanger(); 该重载形式增加了一个隐式的Tleave类型的参数,使当堆上内存不足时,操作符发生Leave。
2、异常退出函数的使用
异常退出处理的系统函数:
(1)User::LeaveIfError():其参数为一个整数值,若其小于0,产生Leave,并将该参数作为异常退出码。用于需要返回非Symbian OS标准错误码;
(2)User::Leave():不检查,直接产生以整数参数为异常退出码的Leave;
(3)Use::LeaveIfNull:以一个指针值为参数,参数为NULL时产生以KerrNoMemory为异常退出码的Leave。
构造函数与析构函数是不允许异常退出的。若允许构造函数失败,则应采用两段构造法以避免异常退出;而析构函数本身就由异常退出引起,若再发生Leave则会导致上个Leave不成功,如果有失败的可能,则应使用一个单独的Leave函数在析构前。
使用异常处理函数时,要注意:对堆上的对象,就使用清除栈解决其可能在Leave后的内存泄露问题;而对栈上的对象,必须保证该对象是无析构函数的,即为T类或R类的对象。
3、使用TRAP和TRAPD捕获异常
TRAPD (result, MayLeave() ); //相当于已定义了result
Tint result;
TRAP (result, MayLeave() ); //与上面的等价
即用于跟踪MayLeave()中产生的异常,并将异常代码号保存在result中,作为参数传递给User::Leave()系统函数。
有时当在同一函数中多次或嵌套使用TRAP,可考虑给其整体加一个函数名,用一个TRAP解决问题,见书P17第二例。对应用程序开发来说,其框架自动提供一个最外层的TRAP。
不应轻易使用将错误码作为返回值的方式,而应使用异常退出,这样代码更小而易于维护。使用异常可以将正常执行流程与错误处理流程明确的分开,使用返回错误方案会带动来TRAP的开销。
使用TRAP是一种昂贵的处理异常退出的方法,影响可执行文件尺寸与代码执行速度,应尽量减少使用。但每个程序中至少应该有一个TRAP来捕捉所有发生的异常退出。
4、LeaveScan
用于检查源代码的每一行,确保不是以L结尾的函数都不会发生异常退出,带有L后缀的函数,确实可能发生异常退出。