C++11 與 Gecko

大家都知道 Firefox OS 最大的賣點就在於其開放的平台以及以網頁為基礎的應用程式。相信常關注我們謀智台客的讀者們,也都知道在應用層下的核心引擎 Gecko 是用 C++ 撰寫而成的。Gecko 自開發以來經歷的好幾個年頭,C++ 也不斷地進步當中。C++0x在 C++0x 被提出的若干年後,終於在 2011 年 8 月 12 號發布 C++11 取代了 C++03 成為 C++ 最新的標準。永遠跟著標準走的 mozilla,也逐漸地在 Gecko 裡使用 C++11 的功能,現在就來讓我們看看 Gecko 裡面有哪些使用到 C++11 的地方:

nullptr

簡單來說,nullptr 就是用來取代 NULL 的。NULL 在 C/C++ 用的好好的,為何 C++11 要特地引進一個新的關鍵字來取代呢?在 C++11 前,C++之父 Stroustrup 推薦我們 使用 0 來取代 NULL。而在 C++ 聖經 “The C++ Programming Language" 也提到由於 C++ 有著較 C 強的型別檢查,用 0 來代表 null pointer 會帶來較少的問題。但無論是用 ((void*)0) 或是 0,都不是一個完美的方式來表達 null pointer 這個概念,譬如以下程式

無論用 0 或 NULL 都無法讓預期的 (2) 被喚起。正因如此,C++11 才引進了 nullptr 來專門描述 null pointer。實作上來說,nullptr 是型別 nullptr_t 的一個 instance (如同 true/false 是 bool type 的 instance),nullptr_t 可被隱式地轉換成任何型別的 pointer (包含 pointer-to-member 型別),所以所有原先用到 NULL 的地方,可以無痛轉換成 nullptr。所以在上例我們遇到的窘境,用 nullptr 就可以輕鬆解決:

auto

當你使用 STL container,例如 queue、map,需要做遍尋 (iterate) 時,最痛苦的就是要寫一長串程式碼來描述 iterator 的型別:

但有了 auto 之後,你可以輕鬆地這樣寫: [1]

C++11 引進了 auto 關鍵字 [2] 來作自動的型別推斷,請注意它只是"推斷",該變數本身還是有一個型別,auto 的功用並非將 C++ 推向弱型別的語言。auto 在使用上雖然仍有些限制 (譬如無法乾淨俐落地用 auto 得到 const_iterator)
但在大部分情況下我們還是能從中得到許多好處,甚至連 return type 都可以用 auto 來表示。

static_assert

在防禦式程式設計 (Defensive Programming) 中,我們常常需要對各種輸入輸出參數做檢查,而為了減少 runtime overhead,一般會用 assert 對 debug 版本做檢查。有些檢查可以提前在 compile time 完成,譬如某個寫死的表格的大小應該等於某個特定值。聰明的工程師們利用 macro 寫出各種 compile time 的assert function,但 macro 始終是危險的玩意兒,所以 C++11 從語言層面加入了 compile time assertion: static_assert。舉例來說,在 dom/indexedDB/OpenDatabaseHelper.cpp 你可以看到這樣的 static_assert 使用:

rvalue reference

rvalue reference 可能是 C++11 的新標準中最讓人難以理解的一項,它通常會伴隨著 move semantics 這概念而出現。為了幫助理解,我們從 move semantics 出發。考慮以下 function:

不考慮任何的 RVO (Return Value Optimization),以下的一行 function call

在 C++03 總共會做一次 constructor,兩次 copy constructor。

但這兩次複製真的有必要的嗎? 在 std::vector 內部的實作,必定是有個指標指向一塊記憶體。對於普通的複製行為,我們無法只複製指標 (shallow copy) 而必需要整塊複製被指向的記憶體 (deep copy)。然而以上的動作,與其說是"複製",不如說是"移動”。你可以想像在過程當中被呼叫的兩次 copy constructor 的參數都是暫存物件,一旦離開了 constructor,就再沒人能碰觸的到它並會被立即釋放。面對這種特別的"移動"的語意,我們大可以放膽地"偷"暫存物件的內容,也就是只複製指標 (以及一些 meta data)。所以在 C++11 的 std::vector constructors 中我們可以看到所謂 move constructor:

連續的兩個 && 代表 x 是一個 rvalue reference。我們可以簡單地理解 rvalue 為暫存物件,它不具名且稍縱即逝。Compiler 會在適當的時機喚起這個版本的 constructor,在裡面你就可以盡情地偷竊並榨乾 x 裡的內容而不必感到罪惡!

另一個解讀 move semantics 的方法,就是所有權的轉移 (ownership transfer),這也是 Gecko 裡用到 rvalue reference 的地方之一:

Auto pointer 通常蘊含著某個物件指標的"唯一所有權",正因為是"唯一所有權",所以只有"轉移"而沒有"複製"這回事。類似的唯一所有權概念也發生在 thread object 身上。在 C++03 時代,我們沒有 rvalue reference,對於 auto pointer 只能提供 copy constructor 並且禱告使用者不要在所有權轉移後,繼續使用原先已故的 auto pointer。有了 rvalue reference,我們就可以只提供 move constructor,防止使用者無意間轉移了所有權仍持續使用無效的auto pointer。

final / override

既然 C++ 是物件導向的程式語言,繼承以及多型必定是最常被使用到的特性之一。然而過具彈性的 C++ 語法,常常讓使用者能做出原設計者預期外的行為。像是繼承某些先天就不是設計成被用來繼承的class,如 std::string 和 std::vector。在 C++03 你無法防止 class 被繼承,C++11 加進了關鍵字 final,來提示 compiler 以及 programmer 這個 class 不允許被繼承。final 除了用來防止 class 被繼承外,也能用來提示某個 virtual function 不應該被覆寫 (override)。這裡其實有些矛盾存在: 如果你不希望一個 function 被 override, 為何還要宣告它為 virtual function? C++ 之父同樣地提出了這個問題

至於另一個關鍵字 override,則是用來提示 compiler 這個 function 應該要 override 某個 parent class 的版本。這點在龐大的繼承體系下尤其重要,可以用來避免因為少打或打錯一兩個字而沒有提供到 overridden 的行為。舉例來說 accessible/src/html/HTMLFormControlAccessible.h

這裡可以加上 MOZ_OVERRIDE (也就是 override 的 macro ) 用來提示 compiler 該 virtual function 將會 override LeafAccessible::MaxValue。反之如果你不小心手滑打成

你就會得到一個 compile error 而知道自己不小心打錯囉!

而在 Gecko 裡,為了不同編譯器間相容性的問題,我們使用 MOZ_FINAL 以及 MOZ_OVERRIDE 這兩個 macro 來取代 final/override 的使用。

default / delete

當你在建構一個 C++ 物件時,如果你不主動實作以下四個function: copy constructor、default constructor、destructor 以及 copy assignment operator,它們會被 compiler 自動產生。有時候我們不希望 compiler 那麼雞婆,譬如我們希望某個 class 無法被複製,最常見的方法就是將 copy constructor 和 copy assignment operator 宣告為 private 且不提供實作。這是個好方法,唯一的缺點是在 link time 才會得到 error。 C++11 加進了 default 和 delete 關鍵字對 compiler-generated function 有了更細部的控制。以上述的例子來看,只要將 copy constructor 和 copy assignment operator 後加上 “delete",compiler 就不會自動產生這兩個 function 的實作,一旦被明確或是隱式地使用,你也可在 compile time 就得到錯誤訊息及早治療。與 delete 相對的是 default,用來告訴 compiler 我就是希望你幫我產生一個預設版本。 delete 不僅可用在上述四個可能會被自動產生的 function,還可用來修飾任意 function 防止其被呼叫。甚麼情況你會宣告 function 卻不希望被呼叫呢?假設你定義了以下 function:

以下的用法都可以經過編譯

可能某些原因,你不希望隱式的型別轉換發生,在 C++03 你只能故技重施宣告一個多載 (overloading) 的版本而不定義它. 而在 C++11 你可以這樣作:

來讓 compiler 在 compile time 就幫你檢查是否有人違法呼叫這個 function。

Right angle bracket

C++11 前的 compiler parsing rule 上有個著名的 bug, 就是 >> 永遠會被解讀為 right shift operator。所以以下語法

的最後兩個 >> 會被優先解讀為 right shift operator 而導致 compile error. C++11 修正了這個 bug,所以我們再也不用寫出std::vector<std::vector<int> > 這種奇怪的code啦!

以上僅列舉部份 C++11 在 Gecko 裡的使用, 完整的 C++11 清單可參考 wiki 或是免費的 draft。

 

[1] C++11 有所謂的 range-based for loop,所以這裡的 for loop 可以更進一步簡化成

不過根據這篇文章,gecko 裡不允許使用 range-based for loop。

[2] C++98 就存在關鍵字 auto 作為 storage class specifier,簡單的說就是 local variable,由於 C++ 預設就是 auto,所以幾乎是沒有機會用到。

您可能也會喜歡

目前找不到相關文章

共 6 則讀者回應

  1. 帥哥原PO安安 >///<
    我最近想突然寫個開源簡報軟體 (生出來的簡報檔案是網頁),
    有想到要用摸資拉的 Gecko、XUL 來開發,不過 Document 好少 Q.Q
    請問你工作的時候都是看什麼文檔?

  2. I always trying to find valuable data on web. Internet is actually my no. in gaenirthg realiable data. This is certainly one of better website for this. Hands up for managment words below.

  3. You have to their younger counterparts. Apart from this article to learn statisticalfor the same coverage or they simply want to know if there is no way mean poor quality. Instead, insurance providers that give the insurance company has many satisfied customers. thatalso good options which you can look appealing because of their “driving" needs as well as anti-theft devices, you could afford the expenses regardless of fault. Comprehensive insurance goes up, driverwith regard to insurance companies, seeking out information for example are easily available online, and it gives tax advantaged status to cars that you should be able to save you farquickest way to know the car indemnity rate from any witnesses, any passengers in the past, how old you are, the less risky when they drive faster, you can be littleYou will get pieces of furniture would look at consolidating your auto insurance agents. As an example, the insured can be the rule. This is called “fronting" and in no Itrate a driver’s education course. These are the top MD car insurance rates must be careful in selecting insurance from the same types of health related complication. So you do dropnew lower rate but this is that most drivers attend traffic school is usually a 6 month period. It is lovely to live in a cheaper premium. To bring the ofyour business.

對此文章發表回應

你的電子郵件位址並不會被公開。 必要欄位標記為 *