理解 NewL ConstructL NewLC ELeave
初學Symbian開發,第一件感覺迷惑的事情是CleanupStack 第二件肯定是隨處可見的NewL,NewLC,ConstructL。
這些函數的出現依然和內存泄漏有關,這是一種被稱爲兩步構造的機制,英文叫Two-phase Construction。
我知道C++裏面的 new 操作符實際上完成2件事,第一根據對象類的大小在堆上分配一塊內存並獲得指向內存的
指針,第二利用指針調用類的構造函數,最後把指針返回。
在Symbian上這樣做是有隱患的,就是當你分配好了內存,但是調用構造函數的時候程序意外退出了,這樣會造成
剛纔分配的內存產生泄漏。只有那些放入CleanupStack的內存,在程序意外結束後會被釋放,new 分配的內存在
調用構造函數之前還沒有被放入CleanupStack呢。
Symbian的設計師爲了解決這個問題,給所有開發者設計了一個編寫程序的定式,這就是Two-phase Construction。
具體是這樣的:
把普通的new 操作分爲2個步驟來進行,第一步只分配內存,當分配的內存被放入cleanupstack後,第二步進行構造。
但是在C++裏 如何阻止編譯器的new操作不調用構造函數呢?這個貌似不可以。。。
於是Symbian的設計者作了個規定,類在構造函數裏不要做任何可能產生異常的操作,只能做那些絕對安全的事情,比如
簡單的變量賦值,然後提供一個名字叫 ConstructL的函數,在這個函數裏做所有類的初始化工作,當然包括那些危險
的可能導致異常的操作。
那麼 Symbian的 類構造就變成了這樣
比如 有一個叫 CFoo 的類,我想聲明一個指針 p,創建一個CFoo的對象賦給 p
CFoo *p = new(ELeave) CFoo();
CleanupStack::PushL(p);
p->ConstructL();
CleanupStack::Pop();
這樣寫是不是有點太羅嗦?每個對象都要用4條語句創建,如果是頻繁使用實在是太麻煩了。
於是Symbian的設計者又作了一個規定,每個類要實現一個NewL的static函數來完成上面的4條語句的工作
class CFoo
{
public:
static CFoo *NewL()
{
CFoo *self = new(ELeave) CFoo();
CleanupStack::PushL(self);
self->ConstructL();
CleanupStack::Pop();
return self;
}
}
有了NewL以後,調用CFoo的類的程序簡化了
CFoo *p = CFoo::NewL();
那麼NewLC又是什麼呢?和NewL有什麼不同?
有些類是這樣的,他們提供一些方法,需要在對象創建完成後執行,但是這些方法也是會產生異常的比如
CFoo 如果有一個方法叫 DoSomethingL()
那麼程序可以這樣寫嗎?
CFoo *p = CFoo::NewL();
p->DoSomethingL();
顯然這樣寫是有問題的正確的寫法是
CFoo *p = CFoo::NewL();
CleanupStack::PushL(p);
p->DoSomethingL();
CleanupStack::Pop();
天啊,又是4條語句,太麻煩了。要知道在NewL結尾我們剛剛把CFoo的指針從CleanupStack裏拿出來,馬上就又放了進去。
是不是可以簡化呢,那好我們再節約2條語句
NewL去掉結尾的CleanupStack::Pop();
static CFoo *NewL()
{
CFoo *self = new(ELeave) CFoo();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
調用去掉CleanupStack::PushL
CFoo *p = CFoo::NewL();
p->DoSomethingL();
CleanupStack::Pop();
Symbian的設計者又規定了,具有以上行爲的NewL 應該叫NewLC。表示指針返回後,沒有從CleanupStack裏取出,你可以繼續調用一個危險的操作,在最後調用CleanupStack::Pop();
我發現 NewL 可以通過調用NewLC實現。
那麼一個符合Symbian設計師的N條規定的類應該這樣寫
class CFoo
{
public:
static CFoo *NewLC()
{
CFoo *self = new(ELeave) CFoo();
CleanupStack::PushL(self);
self->ConstructL();
return self;
}
static CFoo *NewL()
{
CFoo *self = NewLC();
CleanupStack::Pop();
return self;
}
virtual ~CFoo()
{
}
protected:
CFoo()
{
}
void ConstructL()
{
// ....
}
}
這裏注意 CFoo的構造函數不能是Public的,爲了防止使用者用new 或者在棧上創建對象。
析構函數要寫成虛函數,這是純C++問題,不明白的去看 More Effective C++
要說明一下,以上的寫法是Symbian極力推薦的,但是不是硬性規定的,你只要保證沒有內存泄漏
可以不這麼寫。
我個人還是推薦這樣,這樣的代碼寫Symbian程序的人都可以很好地理解。
最後說一下 new 之後爲什麼要有一個 (ELeave)。
new操作符是被Symbian重載過了,ELeave是給new的一個參數,他的意思是告訴new當無法分配內存時
程序就退出。比如內存不足的時候。
所以我們用了ELeave的話 就不用檢查new 返回的指針了,能返回就一定是對的
如果出了錯程序就結束掉了,new根本就不會返回。
NewL NewLC 是Symbian程序標誌性的函數,所以有個Symbian開發的資源站點就叫 www.newlc.com