一、为什么使用清除栈
清除栈主要是用来处理在异常退出发生时那些或许可以称之为被遗弃或泄漏的内存。
看下面的代码:
void UnsafeFunctionL()
{
       CClanger* clanger = new(ELeave) CClanger();
       clanger->InitializeL();
       ……..//略去
        delete clanger;
}

分析:一旦clanger->InitializeL()运行时发生异常,则clanger所指的堆内存将会泄漏,这个例子我们在三步曲之一中提到过。
那么有什么办法来处理这种潜在的错误呢?
我们很容易就想到,可以使用TRAPD捕获异常来进行堆内存的释放,即将上面代码进行如下修改:
void UnsafeFunctionL()
{
       CClanger* clanger = new(ELeave) CClanger();
       TRAPD(error,clanger->InitializeL());
       If(KErrNone != error)
{
               delete clanger;
}
       ……..//略去
       delete clanger;
}
也就是说通过TRAPD捕获异常,然后对异常错误码进行判断,如果确实发生异常了,那么就调用delete来释放堆内存。
当然,上面办法是可行的,但是,如果存在多个可能异常的函数,那么我们都使用TRAPD捕获异常的话,所造成的系统开销就会非常之大,清除栈就是为了解决这个问题而存在的,并且它很好的解决了这样类型的一系列问题。

二、使用清除栈
类CleanupStack定义在头文件e32base.h中,可以通过这个类提供的静态成员函数来访问清除栈。
清除栈的原理:
在调用可能发生异常退出的代码之前,非异常退出安全的对象应该被置于清除栈上,这可以保证在异常退出发生时,这些对象可以被正确的销毁。当发生异常退出时,清除栈能够将所有已经置于其上的对象回收。

下面就是一个使用清除栈的小例子:
void SafeFunctionL()
{
       CClanger* clanger = new(ELeave) CClanger;
       CleanupStack::PushL(clanger);
       clanger->InitializeL();
       clanger->DoSomethingElseL();
       CleanupStack::Pop(clanger);
       delete clanger;
}
实际上这个函数中的最后两条语句
CleanupStack::Pop(clanger);
delete clanger;
可以使用CleanupStack::PopAndDestroy(clanger);这一条语句来替代,它们是等价的。
如果在调用clanger->InitializeL();或clanger->DoSomethingElseL();的时候异常退出了,clanger对象就会被清除栈销毁。

comments:只有当你的调用可能(只要有可能)会导致异常退出,你就必须把它弄到清除栈上去,否则的话(也就是你能相当确认后续的操作不会导致异常发生),完全没有必要,而且如果你这样做的话也是浪费系统资源,清除栈上放一个指针虽然只有4个字节,但也是肉啊!

顺序问题:
对象必须以严格的顺序压入和弹出清除栈,一组Pop()操作必须与PushL()调用的顺序相反。可能会发生调用Pop()或PopAndDestroy()弹出对象而没有命名这些对象的情况,但最好还是要对弹出对象进行命名。
举例:
void ContrivedExampleL()
{
       CSiamese* sealPoint = NewL(ESeal);
       CleanupStack::PushL(sealPoint);
       CSiamese* chocolatePoint = NewL(EChocolate);
       CleanupStack::PushL(chocolatePoint);
       CSiamese* violetPoint = NewL(EViolet);
       CleanupStack::PushL(violetPoint);
       CSiamese* bluePoint = NewL(EBlue);
       CleanupStack::PushL(bluePoint);
      
       sealPoint->CatchMouseL();//入清除栈语句放在可能异常退出的代码之前
      
       CleanupStack::PopAndDestroy(bluePoint);
       CleanupStack::PopAndDestroy(violetPoint);
       CleanupStack::PopAndDestroy(chocolatetPoint);
       CleanupStack::PopAndDestroy(sealPoint);
}

可以看到出栈的顺序和入栈的顺序正好是相反的,不过在这里显得复杂了一点,上面的四个出清除栈语句可以使用 CleanupStack::PopAndDestroy(4);或 CleanupStack::PopAndDestroy(4,sealPoint);进行代替效果基本是一样的。

comments:清除栈,清除栈,也是一个栈啊,栈的操作就是先进后出,所以,最后push到栈上的要先pop出来,先push的最后出来,很简单吧!


创建在堆上的对象由谁销毁?

对于一个对象,清除永远不能超过一次。如果清除栈上有一个指向对象的指针,而后来又保存到其它地方了,譬如成了另一个对象的成员变量,而这个对象在异常退出后仍可以被访问,那么就应该从清除栈上弹出这个指针。如果在清除栈上保留了这个指针,那么清除栈会销毁它所指的对象,但是保存了该指针的对象也会试图通过析构函数销毁这个指针所指的对象。
因此,对象应该只被清除栈或另一个对象所引用,而不能同时被两者引用。类似的,永远不要将类成员变量压入清除栈。
小例子:
void TransferOwnershipExampleL()
{
       CClanger* clanger = new( ELeave ) CClanger();
       CleanupStack::PushL(clanger);//压入清除栈
       iMemberObject->TakeOwnershipL(clanger);//类成员iMemberObject获得了对象所有权
       CleanupStack::Pop(clanger);//这里就必须从清除栈中弹出对象指针
                          //调用完异常代码后将对象指针从清除栈中弹出
}

comments:永远不要将类成员变量压入清除栈,会导致double-deletion

命名问题:
如果有对象被压入清除栈,并直至函数返回时还保留在清除栈上,则该函数应该以”C”作为后缀。这就告诉函数调用者,如果函数正常返回,清除栈上仍然有多余的对象。
CSiamese* CSiamese::NewLC(TpointColor aPointColor)
{
       CSiamese* me = new( ELeave ) CSiamese( aPointColor );
       CleanupStack::PushL( me );
       me->ConstructL();
       return me;
}
上面的函数实际是用在对象的二阶段构造中,其中,压入清除栈后并没有弹出,因此命名时必须要用”C”结尾。

三、对非CBase派生类使用清除栈

看一下CleanupStack::PushL()的三种重载形式:
(1)CleanupStack::PushL(CBase* aPtr)
(2)CleanupStack::PushL(TAny*)
(3)CleanupStack::PushL(TCleanupItem)

第一种形式:用在CBase的派生类对象上,当popanddestroy时,会调用该派生类对象的析构函数,跟C++一样,先调用派生层次最深类的析构函数,然后沿着派生层次顺次向上调用析构函数,最后调用CBase的空析构函数。

第二种形式:如果在定义一个C类时,忘记了从CBase派生,那么就会很危险(千万不要这么做,如果你这么做了,出什么事情,你自己要负全责),因为在调用PushL时,实际上调用的是CleanupStack::PushL(TAny*)这个方法,这样在pop时就不会调用这个类的析构函数,仅仅是清除它所指向的堆内存而已。实际上该方法被用来将没有析构函数的、基于堆的对象的指针压入清除栈(比如T类对象或结构体)。

第三种形式:接收一个TcleanupItem类型对象作为参数,这个函数可以使我们将其他类型的对象或那些具有定制的清除例程的对象压入清除栈。TCleanupItem对象封装了一个指向要保存在清除栈上对象的指针和一个指向提供相应对象清除操作的函数指针。

另外,Symbian OS还提供的三个用于清除的工具函数-- CleanupReleasePushL(),CleanupDeletePushL(), CleanupClosePushL(),分别对应是Release()、Delete()、Close(),都会生成一个TCleanupItem对象让我们能够自己定义清除的过程。并结合下面三个入栈方法。当然,这里入栈的对象引用,可以是创建在堆上的任意对象,比如C类对象,R类对象,T类对象,M类对象等等均可。
(1)       CleanupReleasePushL( T& aRef注意参数不是T*) //

异常退出的处理或PopAndDestroy()调用将对T类对象调用Release()。
举例:
class MExtraTerrestrial
{
public:
          virtual void CommunicateL() = 0;
          …..//出于整洁,略去接口其他代码
          virtual void Release() = 0;
}

class CClanger : public CBase , MExtraTerrestrial
{
public:
          static MExtraTerrestrial* NewL();
          virtual void CommunicateL();
          virtual void Release();
private:
          CClanger();
          ~CClanger();
private:
          ……..
}
void TestMixinL()
{
MExtraTerrestrial* clanger = Clanger::NewL();
CleanupReleasePushL(*clanger);//参数不是指针,这点和普通PushL不同
……..//执行可能发生异常退出的代码
CleanupStack::PopAndDestroy(clanger);//这里是指向对象的指针
}
注意:入清除栈和出清除栈时的参数是不一样的。
(2)       CleanupDeletePushL( T& aRef)
通过使用CleanupDeletePushL()可以使异常退出处理或PopAndDestroy()调用对对象施以delete操作,进而调用对象的析构函数,并且相应的堆内存也会被释放。这就类似于使用接受CBase指针的CleanupStack::PushL()重载函数。当必须要将M派生类指针置于清除栈上时,该函数尤为有用。

(3)       CleanupClosePushL( T& aRef类对象内置了Close()方法,不用另外添加了。) //R
如果对象是通过CleanupClosePushL()压入清除栈的话,则异常退出处理或PopAndDestroy()调用将对对象施以close()操作。
void UseFilesystemL()
{
RFs theFs;
User::LeaveIfError(theFs.Connect());
CleanupClosePushL(theFs);
……..//执行可能发生异常退出的代码
CleanupStack::PopAndDestroy(&theFs);
}

Symbian OS 提供的这三个工具模板函数,它们分别对应于Release()、Delete()、Close()这三个清除方法,这3个工具函数都会生成一个TCleanupItem类型的对象并将其压入清除栈中。

comments:其实是上面的三个函数会生成一个TCleanupItem对象,然后这个对象会自动调用相应的Close()或是其他的函数。

小结:
系统中每个分配了资源的可执行单元(或者线程)都有它自己的清理栈和最高级别的TRAP/TRAPD宏来做异常处理和一些退出后的善后工作。之所以引入清除栈,就是为了解决堆内存泄漏的问题,注意是堆内存,如果对象被创建在了栈上的话,这是不关清除栈的事的,因为栈上的对象所占空间由栈自动管理。

注意:对于C类对象而言,CleanupStack::Pop()方法仅仅是将C类对象指针从清除栈中弹出了而已,并没有调用这个C类对象的析构函数,若要析构,需要再加语句delete c,或者可以直接使用CleanupStack::PopAndDestroy()同时完成上面两个动作。

Symbian 清除栈 CleanupStack相关推荐

  1. Symbian的内存管理机制

    Symbian解决内存泄露的机制:Cleanup Stack 概念: 构造一个全局堆栈,用来记录类对象指针.每实例化一个对象,则将对象指针备份到这个堆栈中,程序go on.最终两种情况:①所有函数执行 ...

  2. Symbian系统开发教程(二)

    第二章:数据类型与语法 作者:谢兴   enigma19971@hotmail.com             转载需注明出处      下载word版本         Symbian系统已经提供了 ...

  3. Symbian手记【二】 —— Symbian对象构造

    [二] Symbian对象构造 C++的纯手工内存管理,确实是一个万恶之源.在对象构造时,有一个著名的内存泄漏隐患问题.比如一个类如下: class A { public:         A()   ...

  4. symbian学习转载

    [虎.无名]学习Blog 诺盛电信咨询:移动支付是什么- -| 回首页 | 2006年索引 | - -Nokia 6681菜鸟必修课 zt 腾达智能社区:Symbian基础类知识- - http:// ...

  5. Symbian知识汇集

    1,Symbian命名法 1) 基本类型      TIntX 和 TUintX (其中X = 8, 16 和 32) 分别用来表示 8位, 16位 和 32位的有符号和无符号的整数. 一般情况下,使 ...

  6. Symbian OS通讯录

    Symbian OS通讯录模型 Symbian OS手机的通讯录采用文件方式存储,用symbian自己的说法就是通讯录数据库.每个Symbian OS手机都有一个默认的通讯录数据库,这个通讯录数据库在 ...

  7. symbian os:描述符

    // // 这是我的第二篇博文,开始学习Symbian c++ // 同大家一起努力 // 谨以此文献给我的最爱 YY女孩(YY 可别想坏了哦) // // --------------------- ...

  8. symbian os 通讯录引擎

    1.1通讯录模型 symbian os手机的通讯录采用文件方式存储,就是通讯录数据库.每个个symbian os手机都有一个默认的通讯录数据库.symbian os 的手机通讯录在开发上的操作依靠sy ...

  9. Symbian OS内存泄漏

    内存泄漏 From Forum Nokia Wiki 内存泄漏 内存管理在Symbian OS下是一个重要的问题.本页描述有关内存泄漏的问题. 两阶段构造阻止内存泄漏 由于Symbian特殊的错误处理 ...

  10. 诺基亚symbian 手册汇编

    Symbian OS Basics  Basic Types 在Symbian中,很多C++基本类型都被重新定义了,最好使用Symbian的,理由如下: 所有Symbian API都是用的Symbia ...

最新文章

  1. cdh的集成phoenix安装_环境篇:Kylin3.0.1集成CDH6.2.0
  2. TabSpec与TabHost
  3. CentOS 7环境安装Docker
  4. UVA1374 Power Calcilus快速幂计算
  5. openkm zip 导入乱码问题解决
  6. 室内装修隐蔽工程验收知识拓展_装修之前先看知贤,『装修微课堂』室内装修隐蔽工程详解,装修小白防坑避雷手册!...
  7. Event用计算机语言,求高人解释下一段计算机语言。
  8. redis linux 删除数据结构,Redis集合数据结构和常用命令
  9. 树莓派竟出微控制器了!Raspberry Pi Pico 只需 4 美元!
  10. dll放在unity哪个文件夹下_unity调用C#dll文件
  11. 【Git/Github学习笔记】Git课程简介
  12. 数据权限设计研究-行数据权限
  13. 微信小程序实现扫一扫功能
  14. vnc以及xfce安装\xrdp连接
  15. 面向数据机密性的云计算脆弱性分析框架
  16. 大学计算机实验报告答案 南京理工大学,南京理工大学微机实验报告
  17. 微信h5支付,微信外浏览器支付实现
  18. html如何让图片不失真,图片放大不失真的几种方法
  19. 阿里独家揭秘:淘宝全站HTTPS 改造细节
  20. 字节跳动 Flink 单点恢复功能及 Regional CheckPoint 优化实践

热门文章

  1. node视频转码框架
  2. 什么样的文案才算是好文案?
  3. 如何创建一个进度条控件
  4. 驱动器存在问题-U盘量产-主控SM3255AB
  5. 基于wincap写抓包程序
  6. 制作纯净的U盘启动盘(避免纯净系统安装后却内置垃圾软件)
  7. java手机飞信_手机飞信JAVA通用版 手机飞信2011通用版
  8. 自我介绍html模板王,个性简短自我介绍模板十篇
  9. 电脑上传,如何查看电脑上传速度
  10. 2020高考数学:常用知识点公式第四章(文科)