Symbian 清除栈 CleanupStack
?
一、為什么使用清除棧
清除棧主要是用來(lái)處理在異常退出發(fā)生時(shí)那些或許可以稱(chēng)之為被遺棄或泄漏的內(nèi)存。
看下面的代碼:
void UnsafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger();
?????? clanger->InitializeL();
?????? ……..//略去
??????? delete clanger;
}
分析:一旦clanger->InitializeL()運(yùn)行時(shí)發(fā)生異常,則clanger所指的堆內(nèi)存將會(huì)泄漏,這個(gè)例子我們?cè)谌角恢刑岬竭^(guò)。
那么有什么辦法來(lái)處理這種潛在的錯(cuò)誤呢?
我們很容易就想到,可以使用TRAPD捕獲異常來(lái)進(jìn)行堆內(nèi)存的釋放,即將上面代碼進(jìn)行如下修改:
void UnsafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger();
?????? TRAPD(error,clanger->InitializeL());
?????? If(KErrNone != error)
{
?????????????? delete clanger;
}
?????? ……..//略去
?????? delete clanger;
}
也就是說(shuō)通過(guò)TRAPD捕獲異常,然后對(duì)異常錯(cuò)誤碼進(jìn)行判斷,如果確實(shí)發(fā)生異常了,那么就調(diào)用delete來(lái)釋放堆內(nèi)存。
當(dāng)然,上面辦法是可行的,但是,如果存在多個(gè)可能異常的函數(shù),那么我們都使用TRAPD捕獲異常的話(huà),所造成的系統(tǒng)開(kāi)銷(xiāo)就會(huì)非常之大,清除棧就是為了解決這個(gè)問(wèn)題而存在的,并且它很好的解決了這樣類(lèi)型的一系列問(wèn)題。
二、使用清除棧
類(lèi)CleanupStack定義在頭文件e32base.h中,可以通過(guò)這個(gè)類(lèi)提供的靜態(tài)成員函數(shù)來(lái)訪(fǎng)問(wèn)清除棧。
清除棧的原理:
在調(diào)用可能發(fā)生異常退出的代碼之前,非異常退出安全的對(duì)象應(yīng)該被置于清除棧上,這可以保證在異常退出發(fā)生時(shí),這些對(duì)象可以被正確的銷(xiāo)毀。當(dāng)發(fā)生異常退出時(shí),清除棧能夠?qū)⑺幸呀?jīng)置于其上的對(duì)象回收。
下面就是一個(gè)使用清除棧的小例子:
void SafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger;
???????CleanupStack::PushL(clanger);
?????? clanger->InitializeL();
?????? clanger->DoSomethingElseL();
???????CleanupStack::Pop(clanger);
?????? delete clanger;
}
實(shí)際上這個(gè)函數(shù)中的最后兩條語(yǔ)句
CleanupStack::Pop(clanger);
delete clanger;
可以使用CleanupStack::PopAndDestroy(clanger);這一條語(yǔ)句來(lái)替代,它們是等價(jià)的。
如果在調(diào)用clanger->InitializeL();或clanger->DoSomethingElseL();的時(shí)候異常退出了,clanger對(duì)象就會(huì)被清除棧銷(xiāo)毀。
comments:只有當(dāng)你的調(diào)用可能(只要有可能)會(huì)導(dǎo)致異常退出,你就必須把它弄到清除棧上去,否則的話(huà)(也就是你能相當(dāng)確認(rèn)后續(xù)的操作不會(huì)導(dǎo)致異常發(fā)生),完全沒(méi)有必要,而且如果你這樣做的話(huà)也是浪費(fèi)系統(tǒng)資源,清除棧上放一個(gè)指針雖然只有4個(gè)字節(jié),但也是肉啊!
順序問(wèn)題:
對(duì)象必須以嚴(yán)格的順序壓入和彈出清除棧,一組Pop()操作必須與PushL()調(diào)用的順序相反。可能會(huì)發(fā)生調(diào)用Pop()或PopAndDestroy()彈出對(duì)象而沒(méi)有命名這些對(duì)象的情況,但最好還是要對(duì)彈出對(duì)象進(jìn)行命名。
舉例:
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();//入清除棧語(yǔ)句放在可能異常退出的代碼之前
??????
?????? CleanupStack::PopAndDestroy(bluePoint);
?????? CleanupStack::PopAndDestroy(violetPoint);
?????? CleanupStack::PopAndDestroy(chocolatetPoint);
?????? CleanupStack::PopAndDestroy(sealPoint);
}
可以看到出棧的順序和入棧的順序正好是相反的,不過(guò)在這里顯得復(fù)雜了一點(diǎn),上面的四個(gè)出清除棧語(yǔ)句可以使用 CleanupStack::PopAndDestroy(4);或 CleanupStack::PopAndDestroy(4,sealPoint);進(jìn)行代替效果基本是一樣的。
comments:清除棧,清除棧,也是一個(gè)棧啊,棧的操作就是先進(jìn)后出,所以,最后push到棧上的要先pop出來(lái),先push的最后出來(lái),很簡(jiǎn)單吧!
創(chuàng)建在堆上的對(duì)象由誰(shuí)銷(xiāo)毀?
對(duì)于一個(gè)對(duì)象,清除永遠(yuǎn)不能超過(guò)一次。如果清除棧上有一個(gè)指向?qū)ο蟮闹羔?#xff0c;而后來(lái)又保存到其它地方了,譬如成了另一個(gè)對(duì)象的成員變量,而這個(gè)對(duì)象在異常退出后仍可以被訪(fǎng)問(wèn),那么就應(yīng)該從清除棧上彈出這個(gè)指針。如果在清除棧上保留了這個(gè)指針,那么清除棧會(huì)銷(xiāo)毀它所指的對(duì)象,但是保存了該指針的對(duì)象也會(huì)試圖通過(guò)析構(gòu)函數(shù)銷(xiāo)毀這個(gè)指針?biāo)傅膶?duì)象。
因此,對(duì)象應(yīng)該只被清除棧或另一個(gè)對(duì)象所引用,而不能同時(shí)被兩者引用。類(lèi)似的,永遠(yuǎn)不要將類(lèi)成員變量壓入清除棧。
小例子:
void TransferOwnershipExampleL()
{
?????? CClanger* clanger = new( ELeave ) CClanger();
?????? CleanupStack::PushL(clanger);//壓入清除棧
?????? iMemberObject->TakeOwnershipL(clanger);//類(lèi)成員iMemberObject獲得了對(duì)象所有權(quán)
?????? CleanupStack::Pop(clanger);//這里就必須從清除棧中彈出對(duì)象指針
????????????????????????? //調(diào)用完異常代碼后將對(duì)象指針從清除棧中彈出
}
comments:永遠(yuǎn)不要將類(lèi)成員變量壓入清除棧,會(huì)導(dǎo)致double-deletion
命名問(wèn)題:
如果有對(duì)象被壓入清除棧,并直至函數(shù)返回時(shí)還保留在清除棧上,則該函數(shù)應(yīng)該以”C”作為后綴。這就告訴函數(shù)調(diào)用者,如果函數(shù)正常返回,清除棧上仍然有多余的對(duì)象。
CSiamese* CSiamese::NewLC(TpointColor aPointColor)
{
?????? CSiamese* me = new( ELeave ) CSiamese( aPointColor );
?????? CleanupStack::PushL( me );
?????? me->ConstructL();
?????? return me;
}
上面的函數(shù)實(shí)際是用在對(duì)象的二階段構(gòu)造中,其中,壓入清除棧后并沒(méi)有彈出,因此命名時(shí)必須要用”C”結(jié)尾。
三、對(duì)非CBase派生類(lèi)使用清除棧
看一下CleanupStack::PushL()的三種重載形式:
(1)CleanupStack::PushL(CBase* aPtr)
(2)CleanupStack::PushL(TAny*)
(3)CleanupStack::PushL(TCleanupItem)
第一種形式:用在CBase的派生類(lèi)對(duì)象上,當(dāng)popanddestroy時(shí),會(huì)調(diào)用該派生類(lèi)對(duì)象的析構(gòu)函數(shù),跟C++一樣,先調(diào)用派生層次最深類(lèi)的析構(gòu)函數(shù),然后沿著派生層次順次向上調(diào)用析構(gòu)函數(shù),最后調(diào)用CBase的空析構(gòu)函數(shù)。
第二種形式:如果在定義一個(gè)C類(lèi)時(shí),忘記了從CBase派生,那么就會(huì)很危險(xiǎn)(千萬(wàn)不要這么做,如果你這么做了,出什么事情,你自己要負(fù)全責(zé)),因?yàn)樵谡{(diào)用PushL時(shí),實(shí)際上調(diào)用的是CleanupStack::PushL(TAny*)這個(gè)方法,這樣在pop時(shí)就不會(huì)調(diào)用這個(gè)類(lèi)的析構(gòu)函數(shù),僅僅是清除它所指向的堆內(nèi)存而已。實(shí)際上該方法被用來(lái)將沒(méi)有析構(gòu)函數(shù)的、基于堆的對(duì)象的指針壓入清除棧(比如T類(lèi)對(duì)象或結(jié)構(gòu)體)。
第三種形式:接收一個(gè)TcleanupItem類(lèi)型對(duì)象作為參數(shù),這個(gè)函數(shù)可以使我們將其他類(lèi)型的對(duì)象或那些具有定制的清除例程的對(duì)象壓入清除棧。TCleanupItem對(duì)象封裝了一個(gè)指向要保存在清除棧上對(duì)象的指針和一個(gè)指向提供相應(yīng)對(duì)象清除操作的函數(shù)指針。
另外,Symbian OS還提供的三個(gè)用于清除的工具函數(shù)--?CleanupReleasePushL(),CleanupDeletePushL(),?CleanupClosePushL(),分別對(duì)應(yīng)是Release()、Delete()、Close(),都會(huì)生成一個(gè)TCleanupItem對(duì)象讓我們能夠自己定義清除的過(guò)程。并結(jié)合下面三個(gè)入棧方法。當(dāng)然,這里入棧的對(duì)象引用,可以是創(chuàng)建在堆上的任意對(duì)象,比如C類(lèi)對(duì)象,R類(lèi)對(duì)象,T類(lèi)對(duì)象,M類(lèi)對(duì)象等等均可。
(1)?????? CleanupReleasePushL(?T&?aRef注意參數(shù)不是T*) //
異常退出的處理或PopAndDestroy()調(diào)用將對(duì)T類(lèi)對(duì)象調(diào)用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);//參數(shù)不是指針,這點(diǎn)和普通PushL不同
……..//執(zhí)行可能發(fā)生異常退出的代碼
CleanupStack::PopAndDestroy(clanger);//這里是指向?qū)ο蟮闹羔?br style="line-height: normal;" />}
注意:入清除棧和出清除棧時(shí)的參數(shù)是不一樣的。
(2)?????? CleanupDeletePushL(?T& aRef)
通過(guò)使用CleanupDeletePushL()可以使異常退出處理或PopAndDestroy()調(diào)用對(duì)對(duì)象施以delete操作,進(jìn)而調(diào)用對(duì)象的析構(gòu)函數(shù),并且相應(yīng)的堆內(nèi)存也會(huì)被釋放。這就類(lèi)似于使用接受CBase指針的CleanupStack::PushL()重載函數(shù)。當(dāng)必須要將M派生類(lèi)指針置于清除棧上時(shí),該函數(shù)尤為有用。
(3)?????? CleanupClosePushL( T& aRef類(lèi)對(duì)象內(nèi)置了Close()方法,不用另外添加了。) //R
如果對(duì)象是通過(guò)CleanupClosePushL()壓入清除棧的話(huà),則異常退出處理或PopAndDestroy()調(diào)用將對(duì)對(duì)象施以close()操作。
void UseFilesystemL()
{
RFs theFs;
User::LeaveIfError(theFs.Connect());
CleanupClosePushL(theFs);
……..//執(zhí)行可能發(fā)生異常退出的代碼
CleanupStack::PopAndDestroy(&theFs);
}
Symbian OS 提供的這三個(gè)工具模板函數(shù),它們分別對(duì)應(yīng)于Release()、Delete()、Close()這三個(gè)清除方法,這3個(gè)工具函數(shù)都會(huì)生成一個(gè)TCleanupItem類(lèi)型的對(duì)象并將其壓入清除棧中。
comments:其實(shí)是上面的三個(gè)函數(shù)會(huì)生成一個(gè)TCleanupItem對(duì)象,然后這個(gè)對(duì)象會(huì)自動(dòng)調(diào)用相應(yīng)的Close()或是其他的函數(shù)。
小結(jié):
系統(tǒng)中每個(gè)分配了資源的可執(zhí)行單元(或者線(xiàn)程)都有它自己的清理?xiàng):妥罡呒?jí)別的TRAP/TRAPD宏來(lái)做異常處理和一些退出后的善后工作。之所以引入清除棧,就是為了解決堆內(nèi)存泄漏的問(wèn)題,注意是堆內(nèi)存,如果對(duì)象被創(chuàng)建在了棧上的話(huà),這是不關(guān)清除棧的事的,因?yàn)闂I系膶?duì)象所占空間由棧自動(dòng)管理。
注意:對(duì)于C類(lèi)對(duì)象而言,CleanupStack::Pop()方法僅僅是將C類(lèi)對(duì)象指針從清除棧中彈出了而已,并沒(méi)有調(diào)用這個(gè)C類(lèi)對(duì)象的析構(gòu)函數(shù),若要析構(gòu),需要再加語(yǔ)句delete c,或者可以直接使用CleanupStack::PopAndDestroy()同時(shí)完成上面兩個(gè)動(dòng)作。
總結(jié)
以上是生活随笔為你收集整理的Symbian 清除栈 CleanupStack的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: [转载]乔布斯十大经典语录
- 下一篇: 获取linux时间 毫秒级,Linux获