Namespaces

Variants
Actions

Please note that as of October 24, 2014, the Nokia Developer Wiki will no longer be accepting user contributions, including new entries, edits and comments, as we begin transitioning to our new home, in the Windows Phone Development Wiki. We plan to move over the majority of the existing entries over the next few weeks. Thanks for all your past and future contributions.

内存泄漏

From Wiki
Jump to: navigation, search
Article Metadata

文章
dougcn 在 25 Jun 2008 创建
最后由 hamishwillee 在 30 May 2013 编辑

内存泄漏

内存管理在Symbian OS下是一个重要的问题。本页描述有关内存泄漏的问题。


两阶段构造阻止内存泄漏

由于Symbian特殊的错误处理机制:“异出(leaving)”,复合类应当用两阶段来构造以阻止内存泄漏的发生。考虑下面C++风格的对象构造:

CExample* example = new CExample();
if (example) {
// Do something with example
}

首先,在Symbian OS可以使用new操作符的一个重载版本: new (ELeave),若没有足够的可用内存时异出(Leave)。这使得直接使用所返回的指针成为可能,而不必进一步测试内存分配是否成功,简化了代码。

CExample* example = new(ELeave) CExample();
// Do something with example

然而,不管是用new还是new (ELeave),因为Symbian的内存异出(Leaving)机制,你必须对内存使用给予特别关注。如果构造函数异出(leave),已经由new操作符分配的内存将发生泄漏,因为没有办法清除它了。因此,C++风格的构造函数与析构函数应该永不异出(leave)。

注意: Symbian OS内存管理的一个关键规则: no code within a C++ constructor (or destructor) should ever leave.

将构造代码分成两个阶段就能避免这个问题:

在第一阶段,执行不会异出(Leave)的代码。实际上,这意味着调用基类构造函数,及不会异出(Leave)的函数等等。一般地,这部分在类的基本构造函数中完成。(当然,若第一阶段的构造函数不异出,那么只使用 new (ELeave)操作符而不用两阶段构造也是恰当的。)

在第二阶段,执行可能发生异出的任何构造部分。实践中,其中包含分配内存、使用诸如文件等资源、调用可能异出的函数等等。一般地,这部分在一个叫ConstructL的类方法中完成。

这两个阶段分开执行,在二者之间,由new操作符分配和构造的对象被压入了清除栈。如果在第二阶段异出(Leave)发生,清除栈就调用析构函数释放已经成功分配的任何资源并销毁给对象本身分配的内存。在Symbian OS中,通用的方式是提供一个包装了二阶段构造的静态函数。一般地,这个函数叫做NewL,或NewLC,后者将构造的对象留在了清除栈中。

这里是一个两阶段构造的例子:

class CExample : public CBase
{
public:
static CExample* NewL();
static CExample* NewLC();
~CExample();
private:
CExample(); // Guaranteed not to leave
void ConstructL(); // Second phase construction code, may leave
};


不会异出的构造函数CExample和第二阶段构造函数ConstructL已被设为私有,这样调用者就不能实例化该类的对象,除了通过NewL (或NewLC)。若你想让你的类子类化,那就应将缺省构造函数设为保护访问(protected)而不是私有(private)访问,这样,编译器就可以构造派生类了。

NewLNewLC的典型实现如下:

CExample* CExample::NewLC()
{
CExample* me = new(ELeave) CExample(); // First phase construction
CleanupStack::PushL(me);
me->ConstructL(); // Second phase construction
return (me);
}
CExample* CExample::NewL()
{
CExample* me = CExample::NewLC();
CleanupStack::Pop(me);
return (me);
}

除了对象构造,你也应仔细考察析构代码。


有关析构函数的问题

必须编写析构函数以释放对象拥有的所有资源。然而,若第二阶段构造失败,清除栈将调用析构函数清理被部分构造的对象。因此,析构代码不能假定对象被完全初始化,你应该清楚通过指针调用函数的问题,该指针可能尚未指向有效对象。

这里是一个安全的析构函数的例子。

CExample::~CExample()
{
if (iMyAllocatedMember) {
iMyAllocatedMember->DoSomeCleanupPreDestruction();
delete iMyAllocatedMember;
}
}


链接

This page was last modified on 30 May 2013, at 04:37.
72 page views in the last 30 days.

Was this page helpful?

Your feedback about this content is important. Let us know what you think.

 

Thank you!

We appreciate your feedback.

×