理解 Symbian C++ 的 NewL ConstructL NewLC ELeave

理解 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

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章