错误的清理和处理(转)

摘要:错误处理和清理工作对于Symbian OS环境下的开发显得尤为重要。错误处理(error handle)实际上是为了编写出更可靠的程序;而清理(cleanup)框架则是Symbian OS的编程基础。正因为如此,必须保证错误处理和清理非常有效且易于实现。

OOM(out-of-memory)错误在Symbian OS环境下则不得不谈。目前的台式机内存的容量越来越大,加上更大硬盘上创建的虚拟内存,而且使用者可能进行经常性的重启。在这样的情况下,内存耗尽很少发生。而在Symbian OS的环境下,RAM少到只有4MB,一般也不会超过16MB,使用时,要求不必重启就可以用很长时间,比如说几个月。所以我们在Symbian OS环境下开发要注意以下问题,也就是平时需要注意的一些原则:

·必须高效编程,使程序不会浪费RAM。
·必须尽快释放资源,如果不释放资源,那么程序将消耗越来越多的RAM。
·必须处理内存不足错误。在Symbian OS中这类错误可以随机发生。
·若出现内存不足错误,导致某些操作停止,绝对不可以使用户数据丢失。
·若某个要分配若干个资源的操作,中途发生内存不足的错误,那么必须清理所有这些资源。

实际上Symbian OS的错误处理和清理框架并不只对OOM错误有效,很多其他操作也可以使用错误处理和清理框架来处理。比如,某些操作因为其环境条件可能失败,读写文件、打开文件,在通信会话中发送和接受数据等。下面列举一些用于处理内存不足错误和测试OOM的工具:

·最普通的调试键常用Ctrl+Alt+Shift+(A使用堆单元、B文件服务器资源、C窗口服务器资源)
·堆检查工具,用来检查函数分配的资源是否释放
·C++的析构函数来销毁对象
·堆失败工具,故意产生错误
·异常退出机制,用来指示错误。其中一个基本函数User::Leave()
·清理栈
·异常捕获装置:用陷阱捕获异常退出的过程,类似与C++中的try-catch机制
·CBase类,所有Symbian OS 类的基类,由清理栈来识别,包含一个C++析构函数
·双阶段构造函数
·命名约定

上述工具在接下来的部分会做一些介绍:
(1)堆检查:主要是使用宏_UHEAP_MARK和_UHEAP_MARKEND。宏_UHEAP_MARKEND主要在析构函数中调用,此时在程序中任何位置如果包含了宏_UHEAP_MARK,则可以检查中间的堆操作是否平衡,就是说如果堆单元的数目和调用_UHEAP_MARK时数量不同,程序就会出错。
(2)析构函数:这个应该不陌生的,只是简单提一下。程序中使用过的对象,并不是所有的都要在析构函数中去释放,它所销毁的只是自己所拥有的对象,也就是不包括那些临时生成的局部变量。
(3)堆失败工具:这个工具主要用来处理内存不足的错误,在有的symbian系统中,有个名为memorymagic的应用程序就是起这个作用的,但不是所有的symbian系统都有这个程序。具体的可以到symbian的官方网站上下载。
(4)异常退出机制:当然就是使用User::Leave()来根据不同的错误码异常退出。User::Leave()函数可以让活动函数的运行终止,并且接着终止所有调用函数。
(5)清理栈: 它解决如下问题:清理栈在堆上分配的,但是指向它的唯一指针却是自动变量的对象。如果分配对象的函数异常退出,则需要清理对象。例程

case EMagicCmd:
{
CX* x = new (ELeave) CX;
CleanupStack::PushL(x);
x->UseL();
CleanupStack::PopAndDestroy(x);
}

在上述例程中,无论UseL()运行是否异常,程序都可以正常退出,因为作为异常处理的一部分,弹出并销毁清理栈上的所有对象是必须完成的。当然你也可以这么做:

case EMagicCmd:
{
CX* x = new (ELeave) CX;
TRAPD(error,x->UseL());
if(error)
{delete x;
User::Leave(error);
}
delete x;
}

注意:如果没有必要,那么就不要使用清理栈,我们只需要使用清理栈来阻止越过对象的析构函数,如果该对象的析构函数一定会被调用,那么就一定不要使用清理栈。一般来说类的成员变量往往都可以被类自身的析构函数销毁,所以绝对不应该把成员变量推入清理栈,避免二次删除!

(6)两阶(two-phase)段构造:清理栈用于保存指向基于堆的对象的指针,以便在发生异常退出时进行清理。这意味着,必须有机会来把对象推入清理栈。这个问题就可以利用两阶段构造来解决。

首先请记住C++的构造函数不应该包含任何可能异常退出的函数。例程:

class CY : public CBase
{
public:
CY();
~CY();
public:
CX* ix;
CY:CY()
{
ix = new(ELeave) CX;
}
CY:~CY()
{
delete ix;
}
}

调用:

// 创建 CY 类实例,并得指向该实例的指针 y
// 在 CY 构造函数中,已经创建了 ix 对象
CY* y = new(ELeave) CY;
// 推入清理栈
CleanupStack::PushL(y);
// 使用
...
// 销毁
CleanupStack::PopAndDestroy(y);


这个时候在分配CY的过程中,同时要分配一个CX,由于构造函数CY()没有异常处理机制,因此一旦分配CX失败,则得到内存泄漏错误,因此需要利用一个完全独立的函数来实现这个。于是就定义了ConstructL():

class CY : public CBase
{
public:
~CY();
static CY* NewL();
static CY* NewLC();
void ConstructL();
public:
CX* ix;
CY:~CY()
{
delete ix;
}
}

void CY:ConstructL()
{
ix = new(ELeave)CX;
}

调用改为:

// 创建 CY 类实例,并得指向该实例的指针 y
// 在 CY 构造函数中,没有创建了 ix 对象

CY* y = new(ELeave) CY;

// 推入清理栈
CleanupStack::PushL(y);

// 在 ConstructL() 中创建 ix 对象
y->ConstructL();
...
CleanupStack::PopAndDestroy(y);

很显然,这时ConstructL()能够安全清理。

一些经验小节:
?对象不可分配两次, 不可删除两次。

delete ib; //加上
ib = NULL; //加上
ib = new CB;

·不要删除非拥有对象
·决不要从C++构造函数中异常退出
·使用好命名规范,帮助自己理解程序。
·用好清理栈。
·用好宏TRAPD()同User:Leave()
-----------------------------------------
总之,Symbian OS下的错误处理和清理是异常复杂和难以琢磨的,只有用我们细心加耐心去慢慢克服。


来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/10294527/viewspace-126408/,如需转载,请注明出处,否则将追究法律责任。

转载于:http://blog.itpub.net/10294527/viewspace-126408/

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值