日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Symbian 清除栈 CleanupStack

發布時間:2023/12/18 编程问答 31 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Symbian 清除栈 CleanupStack 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

?

一、為什么使用清除棧
清除棧主要是用來處理在異常退出發生時那些或許可以稱之為被遺棄或泄漏的內存。
看下面的代碼:
void UnsafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger();
?????? clanger->InitializeL();
?????? ……..//略去
??????? delete clanger;
}

分析:一旦clanger->InitializeL()運行時發生異常,則clanger所指的堆內存將會泄漏,這個例子我們在三步曲之一中提到過。
那么有什么辦法來處理這種潛在的錯誤呢?
我們很容易就想到,可以使用TRAPD捕獲異常來進行堆內存的釋放,即將上面代碼進行如下修改:
void UnsafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger();
?????? TRAPD(error,clanger->InitializeL());
?????? If(KErrNone != error)
{
?????????????? delete clanger;
}
?????? ……..//略去
?????? delete clanger;
}
也就是說通過TRAPD捕獲異常,然后對異常錯誤碼進行判斷,如果確實發生異常了,那么就調用delete來釋放堆內存。
當然,上面辦法是可行的,但是,如果存在多個可能異常的函數,那么我們都使用TRAPD捕獲異常的話,所造成的系統開銷就會非常之大,清除棧就是為了解決這個問題而存在的,并且它很好的解決了這樣類型的一系列問題。

二、使用清除棧
類CleanupStack定義在頭文件e32base.h中,可以通過這個類提供的靜態成員函數來訪問清除棧。
清除棧的原理:
在調用可能發生異常退出的代碼之前,非異常退出安全的對象應該被置于清除棧上,這可以保證在異常退出發生時,這些對象可以被正確的銷毀。當發生異常退出時,清除棧能夠將所有已經置于其上的對象回收。

下面就是一個使用清除棧的小例子:
void SafeFunctionL()
{
?????? CClanger* clanger = new(ELeave) CClanger;
???????CleanupStack::PushL(clanger);
?????? clanger->InitializeL();
?????? clanger->DoSomethingElseL();
???????CleanupStack::Pop(clanger);
?????? delete clanger;
}
實際上這個函數中的最后兩條語句
CleanupStack::Pop(clanger);
delete clanger;

可以使用CleanupStack::PopAndDestroy(clanger);這一條語句來替代,它們是等價的。
如果在調用clanger->InitializeL();或clanger->DoSomethingElseL();的時候異常退出了,clanger對象就會被清除棧銷毀。

comments:只有當你的調用可能(只要有可能)會導致異常退出,你就必須把它弄到清除棧上去,否則的話(也就是你能相當確認后續的操作不會導致異常發生),完全沒有必要,而且如果你這樣做的話也是浪費系統資源,清除棧上放一個指針雖然只有4個字節,但也是肉啊!


順序問題:
對象必須以嚴格的順序壓入和彈出清除棧,一組Pop()操作必須與PushL()調用的順序相反??赡軙l生調用Pop()或PopAndDestroy()彈出對象而沒有命名這些對象的情況,但最好還是要對彈出對象進行命名。
舉例:
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();//入清除棧語句放在可能異常退出的代碼之前
??????
?????? CleanupStack::PopAndDestroy(bluePoint);
?????? CleanupStack::PopAndDestroy(violetPoint);
?????? CleanupStack::PopAndDestroy(chocolatetPoint);
?????? CleanupStack::PopAndDestroy(sealPoint);

}

可以看到出棧的順序和入棧的順序正好是相反的,不過在這里顯得復雜了一點,上面的四個出清除棧語句可以使用 CleanupStack::PopAndDestroy(4);或 CleanupStack::PopAndDestroy(4,sealPoint);進行代替效果基本是一樣的。

comments:清除棧,清除棧,也是一個棧啊,棧的操作就是先進后出,所以,最后push到棧上的要先pop出來,先push的最后出來,很簡單吧!


創建在堆上的對象由誰銷毀?

對于一個對象,清除永遠不能超過一次。如果清除棧上有一個指向對象的指針,而后來又保存到其它地方了,譬如成了另一個對象的成員變量,而這個對象在異常退出后仍可以被訪問,那么就應該從清除棧上彈出這個指針。如果在清除棧上保留了這個指針,那么清除棧會銷毀它所指的對象,但是保存了該指針的對象也會試圖通過析構函數銷毀這個指針所指的對象。
因此,對象應該只被清除棧或另一個對象所引用,而不能同時被兩者引用。類似的,永遠不要將類成員變量壓入清除棧。
小例子:
void TransferOwnershipExampleL()
{
?????? CClanger* clanger = new( ELeave ) CClanger();
?????? CleanupStack::PushL(clanger);//壓入清除棧
?????? iMemberObject->TakeOwnershipL(clanger);//類成員iMemberObject獲得了對象所有權
?????? CleanupStack::Pop(clanger);//這里就必須從清除棧中彈出對象指針
????????????????????????? //調用完異常代碼后將對象指針從清除棧中彈出

}

comments:
永遠不要將類成員變量壓入清除棧,會導致double-deletion

命名問題:
如果有對象被壓入清除棧,并直至函數返回時還保留在清除棧上,則該函數應該以”C”作為后綴。這就告訴函數調用者,如果函數正常返回,清除棧上仍然有多余的對象。
CSiamese* CSiamese::NewLC(TpointColor aPointColor)
{
?????? CSiamese* me = new( ELeave ) CSiamese( aPointColor );
?????? CleanupStack::PushL( me );
?????? me->ConstructL();
?????? return me;
}
上面的函數實際是用在對象的二階段構造中,其中,壓入清除棧后并沒有彈出,因此命名時必須要用”C”結尾。

三、對非CBase派生類使用清除棧

看一下CleanupStack::PushL()的三種重載形式:
(1)CleanupStack::PushL(CBase* aPtr)
(2)CleanupStack::PushL(TAny*)
(3)CleanupStack::PushL(TCleanupItem)


第一種形式:用在CBase的派生類對象上,當popanddestroy時,會調用該派生類對象的析構函數,跟C++一樣,先調用派生層次最深類的析構函數,然后沿著派生層次順次向上調用析構函數,最后調用CBase的空析構函數。

第二種形式:如果在定義一個C類時,忘記了從CBase派生,那么就會很危險(千萬不要這么做,如果你這么做了,出什么事情,你自己要負全責),因為在調用PushL時,實際上調用的是CleanupStack::PushL(TAny*)這個方法,這樣在pop時就不會調用這個類的析構函數,僅僅是清除它所指向的堆內存而已。實際上該方法被用來將沒有析構函數的、基于堆的對象的指針壓入清除棧(比如T類對象或結構體)。

第三種形式:接收一個TcleanupItem類型對象作為參數,這個函數可以使我們將其他類型的對象或那些具有定制的清除例程的對象壓入清除棧。TCleanupItem對象封裝了一個指向要保存在清除棧上對象的指針和一個指向提供相應對象清除操作的函數指針。

另外,Symbian OS還提供的三個用于清除的工具函數--
?CleanupReleasePushL(),CleanupDeletePushL(),?CleanupClosePushL(),分別對應是Release()、Delete()、Close(),都會生成一個TCleanupItem對象讓我們能夠自己定義清除的過程。并結合下面三個入棧方法。當然,這里入棧的對象引用,可以是創建在堆上的任意對象,比如C類對象,R類對象,T類對象,M類對象等等均可。
(1)?????? CleanupReleasePushL(?T&?aRef注意參數不是T*) //

異常退出的處理或PopAndDestroy()調用將對T類對象調用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);//參數不是指針,這點和普通PushL不同
……..//執行可能發生異常退出的代碼
CleanupStack::PopAndDestroy(clanger);//這里是指向對象的指針
}
注意:入清除棧和出清除棧時的參數是不一樣的。
(2)?????? CleanupDeletePushL(?T& aRef)
通過使用CleanupDeletePushL()可以使異常退出處理或PopAndDestroy()調用對對象施以delete操作,進而調用對象的析構函數,并且相應的堆內存也會被釋放。這就類似于使用接受CBase指針的CleanupStack::PushL()重載函數。當必須要將M派生類指針置于清除棧上時,該函數尤為有用。

(3)?????? CleanupClosePushL( T& aRef類對象內置了Close()方法,不用另外添加了。) //R
如果對象是通過CleanupClosePushL()壓入清除棧的話,則異常退出處理或PopAndDestroy()調用將對對象施以close()操作。
void UseFilesystemL()
{
RFs theFs;
User::LeaveIfError(theFs.Connect());
CleanupClosePushL(theFs);
……..//執行可能發生異常退出的代碼
CleanupStack::PopAndDestroy(&theFs);
}

Symbian OS 提供的這三個工具模板函數,它們分別對應于Release()、Delete()、Close()這三個清除方法,這3個工具函數都會生成一個TCleanupItem類型的對象并將其壓入清除棧中。

comments:其實是上面的三個函數會生成一個TCleanupItem對象,然后這個對象會自動調用相應的Close()或是其他的函數。

小結:
系統中每個分配了資源的可執行單元(或者線程)都有它自己的清理棧和最高級別的TRAP/TRAPD宏來做異常處理和一些退出后的善后工作。之所以引入清除棧,就是為了解決堆內存泄漏的問題,注意是堆內存,如果對象被創建在了棧上的話,這是不關清除棧的事的,因為棧上的對象所占空間由棧自動管理。

注意:對于C類對象而言,CleanupStack::Pop()方法僅僅是將C類對象指針從清除棧中彈出了而已,并沒有調用這個C類對象的析構函數,若要析構,需要再加語句delete c,或者可以直接使用CleanupStack::PopAndDestroy()同時完成上面兩個動作。

總結

以上是生活随笔為你收集整理的Symbian 清除栈 CleanupStack的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。