第3章. 清除栈

 

  在异常退出时,由于不会调用对象的析构函数,这样对不需要构造函数的对象(如T类)是安全的,而对构建在堆上的局部变量(包括C类与R类),异常退出时常易引进内存泄露,称为被遗弃的内存(orphaned memory)。

  所以,在调用可能发生异常退出的代码之前,非异常退出安全的对象应该被置于清除栈上,从而保证正确的销毁。清除栈保存了指向在发生异常退出时会被销毁的对象的指针。

  清除栈是Symbian OS特有的一个概念,即关于它的代码是不可移植的。

 

1、使用清除栈

  通过CleanupStack类的静态成员函数来访问清除栈,注意PushL()和Pop()的对应性。可以能过以下三种对应方式来进行压栈和清栈操作:

  CleanupStack::PushL(sealPoint);

 

  CleanupStack::PopAndDestroy(4);    //匿名调用清栈,危险

  CleanupStack::PopAndDestroy(4, sealPoint);  //一个调用完成,有最后一个对象

  CleanupStack::PopAndDestroy(sealPoint);  //一个个对应清栈

  

  对象应该只被清除栈或另一个对象所引用,而不能同时被二者所引用。即避免在该对象的指针被压入栈的同时,又是另一个对象的成员变量,以免当异常退出时,清除栈已将前一对象销毁,而后一对象在Leave后仍存在,就会引起调用前一对象出错。对于这种情况,应该在异常前将前一对象弹出栈。

  多次将对象压入栈或弹出清除栈不会有错误发生,只是要避免清除栈试图重复销毁对象。即重复多次调用PopAndDestroy()。

 

  在函数中,我们一般新建对象并压入清除栈。要避免函数返回了,对象还留在清除栈上的情况(除非该函数需要该功能)。

  当函数需要实现在自身返回时该对象仍保留在清除栈上的功能时,该函数应该以“C”作为后缀。这类函数常作为Cbase派生类的静态函数,用于实例化对象并将其留在清除栈上。

  不过应注意不能直接使用TRAP宏来追踪此类函数(如CExample* pExample = TRAPD(r, Cexample::NewLC());)。因为其返回时TRAP已结束,但并未对分配的对象清栈,这在TRAP宏中是不允许的。清除栈保存了销毁对象的嵌套层次,每一层限制在一个TRAP中,当一个TRAP结束还未清栈会导致E32USER-CBASE71系统错误。对于这种情况,应该保证TRAP在最外层。

  

2、清除栈是如何工作的

  在压栈时,PushL()会分配内存来保存清除指针。当清除栈被创建时,默认分配4个空余位置。在每次新保存时都会申请新的空余位置,所以就算该次分配不成功PushL()产生异常退出,也可以保证对象指针被安全的存储。

  编写GUI应用程序或服务器程序是不需要创建清除栈的,因为在这两种程序中清除栈的创建都是框架代码的一部分。但如果是编写简单的控制台测试程序或是创建一个分离的线程,就需要自己分配一个清除栈了。

 

  CleanupStack::PushL()的三种重载形式:

  PushL(CBase* aPtr):用于CBase派生类对象,在销毁时会对指针施以delete的方式,从而可以调用CBase派生类的虚析构函数。对象销毁过程先调用派生层次最深类(即当前派生类)的析构函数,然后沿派生层次顺次向上调用析构函数,最后调用CBase的空析构函数,从而完成整个销毁过程。

  PushL(TAny*):用于没有析构函数的T类、分配在堆上的struct以及堆描述符对象(即HBufC对象,是基于堆的T类对象)。若C类对象未派生自CBase也将使用该重载形式进行压栈,这样就会导致不能正确销毁。

  PushL(TCleanupItem):用于将其他类型对象或那些具有定制的清除例程的对象压入清除栈。TCleanupItem对象封装了一个指向要清除对象的指针和一个指向提供相应清除函数操作的函数指针。

  对于TCleanupItem类型的对象,有分别对应各自清除方法的3个工具函数:

  CleanupReleasePushL(T& aRef):对应T类对象;

  CleanupDeletePushL(T& aRef):对应M类对象;

  CleanupClosePushL(T& aRef):对应基于栈的文件服务器句柄;

 

3、Tips:关于转型(cast)的说明

  C++的转型操作符由于每个都具有特定的意图,要优于C语言风格的类型转换,因此是推荐使用的。

  在Symbian OS v6.0之前,并不支持C++的转型操作符。为了达到C++的转型操作符的效果,定义了一组宏来完成类型转换,但宏中代码是C语言风格的类型转换。

  Symbian OS v6.1之后的版本,可以直接使用static_cast、const_cast和reinterpret_cast。但是由于dynamic_cast使用的RTTI(运行时的类型识别)机制开销昂贵,所以并不被支持。

 

 

posted on 2008-10-13 14:04  王少敏  阅读(475)  评论(0)    收藏  举报

导航