symbian的清除栈介绍
symbian的清除栈介绍
symbian的清除栈
symbian是c++的子集,它基于C++但不需要C++所有的强大的功能,(事实上也不可能在手机这样的终端上实现
完整的C++所有功能).对于异常处理,symbian没有提供c++那样本地变量常异处理时显示调用析构函数的框架.
Symbian用自己清除栈来完成该工作.
1.不使用清除栈的情况:
aFuntion(){
CBar *pBar = new (ELeave) CBar;
pBar->FooL();
delete pBar;
}
由于FooL是一个可能"离开"(暂时用"离开"来翻译Symbian的错误Leave机制)的调用,一旦发生异常离开,则
delete pBar不能被执行,因为变量pBar本身aFunction的自动变量,会自动清除,但它指向的对象就永远留在了
堆中.经常重启手机的用户可能不多.
清除栈的思想是对于需要在异常离开后必须销毁的对象放入清除栈中,一量发生"离开",则异常处理系统会负
责将栈中的对象可靠地销毁,简言之它是一个需要手动控制的垃圾回收器.需要回收的对象需要手动加入,而且
只有在发生异常时才会自动销毁,否则这些对象一直在栈中,需要手工出栈并销毁.
最常用的模式:
aFunction(){
CBar *pBar = new (ELeave) CBar;
CleanupStack::PushL(pBar);
pBar->FooL();
CleanupStack::Pop();
delete pBar;
}
当pBar对象被构造后,立即被放入清除栈.下面pBar->FooL();调用如果出现异常离开了aFunction函数,因为清除栈
中还保存了变量本身的副本,它和变量pBar指向同一对象,所以它能通过这个变量副本清除掉这个堆上的对象.
如果不出现异常则程序继续向下执行:
CleanupStack::Pop();
delete pBar;
这两句,也就是如果没有发生异常则要手动处理栈中的对象,对于不需要清除栈来保证清除的时候主应该及时
出栈并处理.
因为 CleanupStack::Pop(); delete pBar;非常多地在一起使用,它们被合并成一个函数:
CleanupStack::PopAndDestroy(pBar);
那么什么样的对象需要放入清除栈中保护呢?
如上所述,一个函数中C类的自动变量,除了这个函数中的这个本地指针外,再也没有其它指针指向那个对象,一
旦这个指针随异常离开函数而消失,则它所指的对象就永远留在了堆中,这样的指针需要进入清除栈.
对于不需要析构的T类,一般不需要放入清除栈,除非发生这样特殊的调用:
Txx x = new (ELeave)Txx;
一般来说很少需要在堆中分配T类.所以很少发生这样的调用.
对于R类总是和一个资源相关联,作为自动变量时,无论是在栈中还是在堆中创建,都需要在发生异常时已经构造
的R类需要调用Close方法或reset方法,所以自动变量的R类会经常放入清除栈.
作为一个对象的成员变量没有理由放入清除栈,而应该放在析构函数中执行delete操作.
虽然合理使用清除栈能保证发生异常时已经构造的对象被正确销毁,但有一种情况,清除栈无能为力.即一个对象
在构造到一半时发生异常,因为没有成功构造,当前无法放入清除栈中.那么前一半所点用的资源就无法释放.
由些,引用了另一个概念:二层构建
即在构造对象是第一阶段只做零初始化,不构造任何实际的操作.保证不会发生离开事件,所以在构造方法中除了
零初始化没有任何实际操作.这样产生一个对象的引用后就可以在清除栈中保存它的副本,然后进行第二阶段的再
进行实际的构造,这样一量发生异常清除栈中已经有了该对象的引用.可以进行必要的清除:
//第一阶段构造方法:
CTest:CTest(){
//memner = 0;
}
void CTest::ConstructL() {
//实际需要构造的对象.
}
然后通过两个静态方法 NewLC和NewL来获取实例:
CTest* CTest::NewLC()
{
CTest* self = new (ELeave) CTest; //虽然是new (ELeave)运行符,但实际根本不会离开.
CleanupStack::PushL(self);
self->ConstructL(); //此时如果发生异常,清除栈中已经有了self的指针副本,可以执行必要有清除.
return self;
}
一个LC的方法说明函数可能离开但已经使用了清除栈保护,这个方法就是调用以后引用已经放入清除栈了.不需要
再做Push操作.
当然NewLC的目的是为了防止对象构造时发生异常而不能清理,一旦构造成功返回了.我们在对它的引用时不再需要
清除栈的保护,这时需要手出栈,那么NewL就是完成这个操作的:
CTest* CTest::NewL()
{
CTest* self = CTest::NewLC();
CleanupStack::Pop(self);
return self
}
所以两阶段操作可以保证对象在构造的过程中发生异常时能被正确地清理.而提供NewLC函数的类一经构造就已经在
清除栈中了.我们只需要在必要时出栈并销毁就行了:
HBufC* welcom = CEikonEnv::Static()->AllocReadResourceLC(R_WELCOM_TEXT);
//可能离开的操作
CleanupStack::PopAndDestroy(welcom);