虽然Symbian使用的是c++语言,但是由于手机系统的硬件限制,与c++语言差别还是比较大,Symbian定义了一套自己的东西。
主要是对象的二阶段构造,清除栈。
1.堆和栈。
堆资源比较大,可以动态分配内存空间,在堆上创建的对象,使用以后都要显式的清除。
一般使用new来在进程的默认堆上分配一个空间。如果内存不足就会报一个异常。一个小列子
//CMyClass类定义
class CMyClass : public CBase
{
public:
CMyClass();//构造函数
~CmyClass();//析构函数
void Function();//普通函数
}
//在堆上创建cmyclass类的对象
CMyClass* ptr = new CMyClass;
if(ptr != null)
{
ptr->Function();//调用方法
}
delete ptr;//释放指针指向的内存空间
ptr =null;//设置指针为null
在上面代码中,创建类的对象的时候有可能因为内存不足而报错,所以要先检查ptr是不是null,然后才可以调用function 方法。
Symbian中提供了一个ELeave宏,并且重载了new操作符。使用new(ELevae)创建对象的时候,如果内存分配失败,创建的函数将不再执行后面的代码,而是立即退出。所以使用new(ELeave)创建对象,可以省略检查对象是不是为null的步骤
CMyClass* ptr = new CMyClass;
ptr->Function();//调用方法
delete ptr;//释放指针指向的内存空间
ptr =null;//设置指针为null
-----------------------------------------------------------
在说说栈(stack)。栈上的对象也称为自动变量,因为他不用手工去创建和释放空间。只是栈的空间很小,Symbian系统的栈只有8kb。所以只用来创建简单的字段或者对象。
2.清除栈(CleanUp Stack)
清除栈的作用就是保证在异常发生的时候释放堆上的对象。
还使用上面的列子,看一个函数方法
void SomeClass::FunctionL()
{
TInt integer;
CMyClass* object = new(ELeave)CMyClass;
object->Function();
delete object;
object =null;
}
CMyClass* object = new(ELeave)CMyClass;如果这个步骤没有成功,异常的话,integer和object都会释放内存资源的,不会发生内存泄漏的问题。
但是如果生成类的步骤成功,但是在执行Function()的时候错误,发生异常了。这个Function()方法会立刻返回,integer和object会被自动释放,但是object指向的CMyClass对象失去了唯一可以访问他的指针,这个对象的空间成为系统的内存漏洞。
避免这个情况的方法就是使用清除栈。CleanupStack。原来就是将可能异常退出的对象,在他执行之前将对象的指针压入这个清除栈。使用CleanupStack::PushL()函数。
void SomeClass::FunctionL()
{
TInt integer;
CMyClass* object = new(ELeave)CMyClass;
//后面的代码可能有一场,会丢失object,造成object执行的cmyclass不可访问,所以先把他压入栈
CleanupStack:PushL(object);
object->Function();
//程序没有异常,将object对象弹出清除栈
Cleanup::Pop();//弹出栈但是没有清除资源
delete object;
object =null;
}
(CleanupStack主要3个方法PushL,Pop,PopAndDestroy//弹出并清除了资源)
3类对象的两阶段构造
Symbian提供的两阶段构造方法,配合清除栈,保证异常退出时候系统不会产生内存泄漏。
3。1原理。
c++中new方法完成两个功能:一个是在堆上申请空间,然后就是执行构造函数完成对对象的构造。
所以在Symbian中的重要规则就是:
第一阶段使用new(Eleave)申请对象空间。并且调用该类的默认构造函数,而且这个构造函数必须是没有异常发生的
第二阶段讲对象压入清除栈,只有调用类的ConstructL()方法,进行一些可能发生异常的操作
所以一个标准的C类应这样定义:
class CClass1 : CBase
{
public:
CClass1();
~CClass1();
void ConstructL();//新增这个函数,里面执行可能有异常发生的操作
private:
CClass1* member;
}
CClass1::CClass1()//构造函数执行肯定不会有异常发生的操作
{
}
CClass1::ConstrunctL()//执行可能会有异常发生的操作
{
member = new(ELeave)CClass1;
}
CClass1::~CClass1()
{
delete member;
}
这样,调用这个类的时候,可以这样写。就是两个阶段的构造
CClass1* obj = new(ELeve)CClass1;
CleanupStack::PushL(obj);
obj->ConstructL();
...
CleanupStack::PopAndDestroy();
-----------------------------------------------------------------------
但是这样写有点麻烦。就是生成类的对象的时候,每次都要写4行代码。所以要进一步封装
Symbian的规范是使用静态的newL()和newLC()。然后吧构造函数报刊constructL都变成私有的函数。这样写:
class CClass1 : CBase
{
public:
static CClass1* newL();
static CClass1* newLC();
~CClass1();
private:
CClass1();
void ConstructL();//新增这个函数,里面执行可能有异常发生的操作
private:
CClass1* member;
}
CClass1* CClass1::NewLC()//没有执行出栈操作。所以调用newLC以后,要调用一下出栈函数
{
CClass1* self = new(ELeave)CClass1;
CleanupStack::PushL();
self->ConstructL();
return self;
}
CClass* CClass::NewL()//newl方法调用newLC方法,包含了出栈操作。
{
CClass1* self = CClass1::NewLC();
CleanupStack::Pop();
return self;
}
本文使用Blog_Backup未注册版本导出,请到soft.pt42.com注册。