4. 來自 Google 的奇技

Google 用了很多自己實作的技巧 / 工具使 C++ 程式碼更加健壯, 我們使用 C++ 的方式可能和你在其它地方見到的有所不同.

4.1. 所有權與智慧指標

Tip

動態分配出的對象最好有單一且固定的所有主(onwer), 且通過智慧指標傳遞所有權(ownership).

定義:

所有權是一種登記/管理動態內存和其它資源的技術。動態分配出的對象的所有主是一個對象或函式,後者負責確保當前者無用時就自動銷毀前者。所有權有時可以共享,那麼就由最後一個所有主來負責銷毀它。甚至也可以不用共享,在程式碼中直接把所有權傳遞給其它對象。

其實你可以把智慧指標當成一個重載了 *-> 的「對象」來看。智能指針類型被用來自動化所有權的登記工作,來確保執行銷毀義務到位。std::unique_ptr 是 C++11 新推出的一種智能指針類型,用來表示動態分配出的對象的「獨一無二」所有權;當 std::unique_ptr 離開作用域,對象就會被銷毀。不能複製 std::unique_ptr, 但可以把它移動(move)給新所有主。std::shared_ptr 同樣表示動態分配對象的所有權,但可以被共享,也可以被複製;對象的所有權由所有複製者共同擁有,最後一個複製者被銷毀時,對象也會隨著被銷毀。

優點:

  • 如果沒有清晰、邏輯條理的所有權安排,不可能管理好動態分配的內存。
  • 傳遞對象的所有權,開銷比複製來得小,如果可以複製的話。
  • 傳遞所有權也比「借用」指標或引用來得簡單,畢竟它大大省去了兩個使用者一起協調對象生命週期的工作。
  • 如果所有權邏輯條理,有文檔且不亂來的話,可讀性很棒。
  • 可以不用手動完成所有權的登記工作,大大簡化了程式碼,也免去了一大波錯誤之惱。
  • 對於 const 對象來說,智慧指標簡單易用,也比深度複製高效。

缺點:

  • 不得不用指標(不管是智慧的還是原生的)來表示和傳遞所有權。指針語義可要比值語義複雜得許多了,特別是在 API 裡:你不光要操心所有權,還要顧及別名,生命週期,可變性(mutability)以及其它大大小小問題。
  • 其實值語義的開銷經常被高估,所以就所有權的性能來說,可不能光只考慮可讀性以及複雜性。
  • 如果 API 相依性所有權的傳遞,就會害得客戶端不得不用單一的內存管理模型。
  • 銷毀資源並回收的相關程式碼不是很明朗。
  • std::unique_ptr 的所有權傳遞原理是 C++11 的 move 語法,後者畢竟是剛剛推出的,容易迷惑開發者。
  • 如果原本的所有權設計已經夠完善了,那麼若要引入所有權共享機制,可能不得不重構整個系統。
  • 所有權共享機制的登記工作在運行時進行,開銷可能相當不小。
  • 某些極端情況下,所有權被共享的對象永遠不會被銷毀,比如引用死循環(cyclic references)。
  • 智慧指標並不能夠完全代替原生指針。

4.2. cpplint

Tip

使用 cpplint.py 檢查風格錯誤.

cpplint.py 是一個用來分析源文件, 能檢查出多種風格錯誤的工具. 它不並完美, 甚至還會漏報和誤報, 但它仍然是一個非常有用的工具. 在行尾加 // NOLINT, 或在上一行加 // NOLINTNEXTLINE, 可以忽略報錯。

某些專案會指導你如何使用他們的專案工具運行 cpplint.py. 如果你參與的專案沒有提供, 你可以單獨下載 cpplint.py.

譯者(acgtyrant)筆記

  1. 把智慧指標當成對象來看待的話,就很好領會它與所指對像之間的關係了。
  2. 原來 Rust 的 Ownership 思想是受到了 C++ 智慧指標的很大啟發啊。
  3. scoped_ptrauto_ptr 已過時。 現在是 shared_ptruniqued_ptr 的天下了。
  4. 按本文來說,似乎除了智慧指標,還有其它所有權機制,值得留意。
  5. Arch Linux 使用者注意了,AUR 有對 cpplint 打包。