來自 Google 的奇技 -------------------------------------- 我們使用了很多自己實作的技巧和工具,讓 C++ 的程式碼更加穩定可靠,我們使用 C++ 的方式可能和你在其它地方見到的有所不同。 所有權與智慧型指標 (Ownership and Smart Pointers) ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ .. tip:: 動態配置的物件最好擁有單一且固定的管理者 (owner)。欲傳遞所有權,最好使用智慧型指標傳。 定義: 「所有權」是一種登記、管理動態配置的記憶體(和其他資源)的技術。動態配置的物件的管理者是一個物件或函式,負責在物件不再需要的時候將物件刪除。所有權有時可以共享,此時通常最後一個管理者要負責刪除該物件。當所有權沒有共享的時候,甚至可以在不同的程式碼之間傳遞。 「智慧型指標」指的是:藉由多載 ``*`` 和 ``->`` 等運算子,讓行為看起來和指標相似的類別。有些智慧型指標型別可以自動管理所有權,以確保該做的事都會完成。`std::unique_ptr `_ 是 C++11 新增的智慧型指標,用來表示動態配置的物件有獨一無二的所有權;當 ``std::unique_ptr`` 物件離開作用域時,所指物件就會被刪除。``std::unique_ptr`` 無法複製,但可以被 *移動*,用來表示所有權的轉移。`std::shared_ptr `_ 是用來表示動態配置的物件所有權可以被分享的智慧型指標。``std::shared_ptr`` 可以被複製;所有複製出來的 ``std::shared_ptr`` 物件共享所指物件的所有權。當最後一個 ``std::shared_ptr`` 物件被銷毀時,所指物件才會被刪除。 優點: * 如果沒有適當的所有權安排,幾乎不可能妥善管理動態配置的記憶體。 * 就算是物件可以被複製,傳遞物件的所有權所需的運算資源比複製來得小。 * 傳遞所有權也比「借用」指標或 reference 來得簡單,畢竟它省去了協調兩邊管理物件生命週期的工作。 * 智慧型指標可以明確表明所有權的邏輯,自我說明,且不會有模稜兩可的狀態,大大增進了可讀性。 * 智慧型指標為我們省去了手動管理所有權的工作,簡化了程式碼,也避開了大量可能發生的錯誤。 * 對於 const 物件來說,共享管理權可以既簡單又有效率地取代物件的深度複製 (deep copy)。 缺點: * 所有權必須透過指標(不管是智慧型還是原始的)傳遞。指標的語意比純值複雜多了,尤其是在 API 中:除了所有權外,你還得留意別名 (aliasing)、生命週期、可變性 (mutability),以及其他大大小小的問題。 * 純值語義對執行效率的影響常被高估,因此改用所有權轉移所能得到的效能提昇,可能不足彌補在可讀性以及複雜度方面的損失。 * 使用轉移所有權的 API,會讓客戶端不得不使用特定的記憶體管理模型。 * 使用智慧型指標時,資源會在哪裡被釋放變得比較不明顯。 * ``std::unique_ptr`` 利用 C++11 的 move 語意來傳遞所有權。Move 語意相對較新,有些程式員可能不太熟悉。 * 對於所有權設計已經夠完善的系統來說,共享所有權機制雖然看來誘人,但硬要導入可能會搞亂原本的系統設計。 * 所有權共享機制的登記工作在執行時進行,對執行效率可能有不小的衝擊。 * 某些情況(像是循環參考 cyclic references)下,所有權被共享的物件可能永遠不會被刪除,。 * 智慧指標並不能夠完美取代原始指標。 結論: 如果需要動態配置的話,儘可能把所有權保持在配置記憶體的那段程式碼中。如果其他地方也需要存取該物件的話,考慮複製一份物件,或是傳遞物件的指標或 reference,不要傳遞所有權。最好使用 ``std::unique_ptr`` 明確表達所有權的轉移。舉例來說: .. code-block:: c++ std::unique_ptr FooFactory(); void FooConsumer(std::unique_ptr ptr); 除非有很好的理由,否則不要使用所有權共享。其中一個可能的理由是為了避免很花時間的複製行為,但你只應該在省下的時間非常顯著、而且所指的物件是不會改變的(換句話說,就是 ``std::shared_ptr``),才考慮這麼做。如果你真的要共享所有權,建議使用 ``std::shared_ptr``。 禁止使用 ``std::auto_ptr``。請改用 ``std::unique_ptr``。 cpplint ~~~~~~~~~~~~~~~~~~~~~~~~ .. tip:: 使用 ``cpplint.py`` 檢查風格錯誤。 ``cpplint.py`` 是一個用來分析原始碼並檢查出多種風格錯誤的工具。它不並完美,甚至還會漏報或誤報,但它仍然是非常有用的工具。在行尾加 ``// NOLINT`` 或在上一行加 ``// NOLINTNEXTLINE`` 可以忽略誤報。 某些專案會有如何使用他們的專案工具運行 ``cpplint.py`` 的說明。如果你參與的專案沒有提供,你可以單獨下載 `cpplint.py `_。