了解函数是否会异常退出非常重要。任何可能异常退出的函数都应该有一个以L结尾的函数名。所以有RunL()、HandleCommandL()等这样的函数。 如果正在编写一个函数,并且这个函数调用另一个可能异常退出的函数,那么必须在这个函数名称的末尾加上一个L。提程序员自己、同事以及阅读代码的所有人,这个函数可能会异常退出。下面是需要考虑的一些情况:
- 如果你正在编写的函数调用了另一个函数,而且这个函数是一个L函数,那么编写的这个函数必定也要是一个L函数(除非捕获它的异常退出)。
- 如果函数调用new (ELeave),那么函数也必定是一个L函数。
- 如果正在实一个由框架提供的函数,那么就必须了解这个框架提供的函数是否为一个L函数。如果是,则可以分配可能异常退出的资源。如果不是,代码就决不能异 常退出。或者,如果是,必须自己处理异常退出。例如,CEikAppUi::HandleCommandL()函数允许异常退出,所以用户命令处理器可以 分配资源,且有可能异常退出。CCoeControl::Draw()函数不允许异常退出,所以编写的任何绘图代码都必须使用预先分配的资源,而且不能异 常退出。或者,如果因为程序本身的原因,使得它不能预先分配资源,那么必须自行捕获潜在的异常退出并进行处理。
- 如果为其他函数的实现指定了一个框架函数,必须仔细考虑是否允许实现者异常退出。这就是函数的基本特征。不要只是编写一个L函数,让实现者完成希望完成的任何事。仅在需要时才编写L函数,不要编写没有必要的L函数。
与Java和标准C++里的throws子句一样,L命名约定也给编程者提供了类似的信息。不过,编译器不会检查L,所以,如果忘了在函数的名字里面加上 L,编译器也毫无提示。不过,如果其他人使用了这个函数,却没有注意到它可能异常退出,问题就出现了,因为他们可能不会采取任何适当的预防措施。 char* pChar = new(ELeave) char[]; CObject* pObject = new(Leave) CObject(); 对char的内存开辟,是没有什么问题的,对CObject,是有问题的,假设CObject中有两个成员变量,a和b,都是需要内存分配的,在 CObject的构造函数中,先给a分配了内存,再给b分配内存时,出现异常,这时,由于CObject还没有构建完毕,异常出现时CObject的析构 函数不会被调用,故a的内存被泄露。 怎么办?Symbian的设计者设计出了这样一套方案:所以的类,都需要继承自CBase,并重载NewL和NewLC、ConstructL方法,所有 构造函数中可能产生异常的操作,均要放到ConstructL中进行。用户使用该类时,需要通过NewL或者NewLC方法进行构造,不再使用new。 NewL和NewLC的代码如下: static CFoo *NewL() { CFoo *self = new(ELeave) CFoo(); CleanupStack::PushL(self); self->ConstructL(); CleanupStack::Pop(); return self; } static CFoo *NewLC() { CFoo *self = new(ELeave) CFoo(); CleanupStack::PushL(self); self->ConstructL(); return self; } 这样做的好处时,类的构造函数不再进行任何产生异常的操作,故不会产生异常,只要类的内存分配成功,则对象建立就成功了,这样即使在ConstructL中产生异常操作,程序也会调用类的析构函数,将内存删除。 可以看到,如果在new出了对象之后,如果还要进行一些可能产生异常的操作,则使用NewLC较好,如果不进行可能产生异常的操作,则使用NewL较好。 同理可以推论出,对于类里面的成员变量,使用NewL比较好,因为即使操作异常,类的析构函数会将成员删除,故不需要清除栈,但对于函数里面的临时变量, 则使用NewLC比较好(最后使用CleanStack::PopAndDestroy () 进行删除)。 |