×
Namespaces

Variants
Actions

内存泄露

From Nokia Developer Wiki
Jump to: navigation, search
Article Metadata

文章
kcomex 在 04 Apr 2007 创建
最后由 hamishwillee 在 30 May 2013 编辑
Symbian系统的内存管理方式是该操作系统的一个重要特点,下面的文章讨论了和内存泄露有关的问题。

预防内存泄露的二次构造流程

一位Symbian系统独有的错误处理机制,所以符合类型的类需要使用二次构造的方法来防止内存泄露。下面我们用如下的C++代码来看看这种机制的实现方式:

CExample* example = new CExample();
if (example) {
// 对example对象进行操作
}

首先,在Symbian系统中,我们其实仍然是可以使用new关键字的重载版本new(ELeave)的,这个重载版本会在内存不足时进行退出(leave)处理。那么,我们可以直接使用下面的代码,对新分配的对象进行操作,而并不需要在分配之后人为地显式检测对象内存分配的成功与否。

CExample* example = new(ELeave) CExample();
// 对example对象进行操作

尽管如此,我们还是需要因为Symbian系统的特别的退出机制,尤其留心使用new(ELeave)的情况。如果一个对象的构造函数退出了,那么由new关键字操作符所分配的资源将会泄露,因为C++的标准构造函数是永远不会失败的,所以在这里被分配的资源,失败的部分是永远无法得到回收的。

请注意: Symbian操作系统内存管理的一个关键法则:一定不要在标准C++构造函数中使用任何可能导致退出(leave)的函数。

那么,上面的问题就可以通过将构造函数一分为二,实现两个步骤的构造过程,这样进行解决:

• 在构造函数的第一阶段,我们执行的所有代码都是无法产生退出的。实际上的情况就是,我们只在构造函数中调用基类的构造函数,调用不会产生退出的函数,以及和前二者类似的操作。这个部分通常仅仅进行的都是最基本的构造操作。(很自然地,析构函数其实也完全不能使用有可能退出的函数,所以我们应该尽量使用二次构造方式而不是标准new关键字操作符的构造方式。)

• 在构造函数的第二阶段,我们可以任意使用可能退出的函数。实际上的情况包括分配内存资源,使用诸如文件等等的资源,调用可能退出的函数操作等等。而这个部分,我们通常把它叫做ConstructL。

上面的两次构造步骤是分步进行的。在两个步骤之间,第一步由new操作符构造的对象将被推入到Cleanup堆栈当中,一旦在第二阶段进行的构造操作发生了退出(leave),那么Cleanup堆栈将会调用刚才被推入的对象的析构函数,以此来释放第一阶段中被成功分配的内存资源等。那么在Symbian系统中,一个被经常使用的技巧就是使用静态函数来包装这样的二次构造操作,这个函数通常被称为NewL;如果构造函数可能在第二个阶段退出的话,那么这个函数也可以叫做NewLC。

下面就是一个二次构造的例子:

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

这样,无法退出的标准构造函数,CExample,以及对象之外的代码无法访问的私有的二次构造函数ConstructL就构成了二次构造过程的全部内容。如果你的类仍有可能被子类其他继承,那么我们可以将标准构造函数设定为保护(protected)类型,这样子类才有可能继承父类的标准构造函数。

下面就是NewL和NewLC函数的一个实例演示:

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);
}

除了对象构造之外,我们还要对对象析构删除操作特别的留意。

有关析构函数的问题

一个合格的析构函数,必须将对象自己拥有的内存资源完全释放干净。但是景观如此,如果二次构造函数失败了,Cleanup堆栈机制会将把没有构造成功的资源清除干净,因此这个时候析构函数就不能假定构造的资源是已经被分配好了的,这个时候我们需要先判断内存资源是不是已经被分配了,如果是才进行清除资源工作。

这里就是代码范例:

CExample::~CExample()
{
if (iMyAllocatedMember) {
iMyAllocatedMember->DoSomeCleanupPreDestruction();
delete iMyAllocatedMember;
}
}
This page was last modified on 30 May 2013, at 07:41.
211 page views in the last 30 days.
×