iT邦幫忙

第 11 屆 iThome 鐵人賽

DAY 5
2
Software Development

山姆大叔談 C++:從歷史談起,再給個定義—Modern C++ 解惑系列 第 5

DAY 4:只能死一次,不能鞭屍,談 std::unique_ptr<T>,卷二

前一篇std::unique_ptr<T> 的基礎應用,這篇談稍微進階一點的用法。進入主題之前,照慣例要岔一下題。

「Garbage Collection(垃圾回收機利)」是許多「中生代」程式語言用來減輕開發者痛苦的手段。其標榜的「記憶體自動管理」聽起來很美好,卻是在效能上做出「妥協」。實務上,應用程式面對的執行環境多變且複雜,因此,截至目前為止,各大「垃圾回收處理廠演算法」一直無法周全。

std::unique_ptr 用於管理平台核心物件

現今主流的作業系統有 Windows, macOS and Linux/Unix,每個平台都有專屬的系統服務以及相應的 SDK 供開發者使用。而各平台的 SDK 有其特有的記憶體管理方式,或者核心物件(Kernel Object)的管理方式。

C++ 是一個跨平台的開發語言,其設計盡量避免與特定平台相依(綁定),因此,上述平台專屬的資源管理方式,不會納入 C++ 標準規格裡。

所幸,C++ Template 特性,讓 STL 介面設計極具彈性,以 std::unique_ptr<T> 為例,雖然多數時候只指定一個 Template Parameter T,其介面其實接受兩個 Template Parameter,第二個叫做 Deleter,表示刪除其管理物件的方法,而這個方法可以「客制化」——使用開發者自己提供的 Deleter

以常用的 Windows API CreateFile 為例,函數成功時的回傳值為一核心物件,其型別為 HANDLE,釋放該物件的方式是呼叫另一個 Windows API CloseHandle。那麼,我們要如何讓 std::unique_ptr<T> 具備管理 Windows 核心物件的能力呢?就是利用前述的第二個 Template Parameter,也就是 Custom Deleter,寫法如下:

auto close_handle = [](HANDLE h) { CloseHandle(h); }

auto scoped_file = std::make_unique<HANDLE,
                                    decltype(close_handle)
                                    >(CreateFile(...), close_handle);

礙於篇幅,CreateFile 的參數以 ... 簡略標記。

有了 std::unique_ptr 協助管理核心物件,只要離開 Scope(例外也包含其中),核心物件保證會被清除,避免產生資源洩漏(Resource Leak)。

std::unique_ptr Custom Deleter 的設計,讓開發者依需求自訂專屬的「輔具」,用以管理特殊資源。

擁有權以及轉移

std::unique_ptr 的語義很清楚——一個物件只能被刪除一次,交給我,你放心。

但有些時候,我們希望交由 std::unique_ptr 管理的資源,在特定條件成立時,改由其他人管理,也就是令其交出「擁有權(Ownership)」。擁有權移轉的作法有好幾個,簡述如下:

auto tea_owner = std::make_unique<TeaShopOwner>();

if (tea_owner->HitByCCPDirtyTrick()) {
    TeaShopOwner* ccp = tea_owner.release();
    delete cpp;
}

留意上述呼叫 release() 用的是 operator.,而不是 operator->。前者是呼叫 std::unique_ptr 的成員函數,後者則是呼叫「被管理的物件的成員函數」,此例為 TeaShopOwnerHitByCCPDirtyTrick

上述方法利用 std::unique_ptrrelease 成員函數,回傳其管理的原始物件指標,此舉等於交出擁有權,在離開 Scope 時不會刪除物件。呼叫端必須自行管理該物件。

第二個方法使用 C++11 的另一項功能(之後會有更一步的說明)——std::move。此方法可用於在兩個 std::unique_ptr 間轉移,常見於函數參數傳遞:

auto tea_owner = std::make_unique<TeaShopOwner>();

if (tea_owner->HitByCCPDirtyTrick()) {
    TransferOwner(std::move(tea_owner));
}

一旦弄清 std::unique_ptr 的擁有權觀念,在撰寫穩定程式碼會有很大助力。

延伸閱讀


上一篇
DAY 3:只能死一次,不能鞭屍,談 std::unique_ptr<T>,卷一
下一篇
DAY 5:Smart Pointer std::shared_ptr<T>,卷一
系列文
山姆大叔談 C++:從歷史談起,再給個定義—Modern C++ 解惑26
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言