Effective Modern C++ === ## 條款 19:使用 std::shared_ptr 管理共享所有權的資源 - shared_ptr 透過 **Reference Count (RC)** 來判斷是不是最後一個指向該 resource 的 pointer,如果 RC = 0 的話,就清除 resource。RC 的存在使得 shared_ptr 有一些效能上的問題,如下: 1. **shared_ptr 是 raw pointer 的兩倍大**:因為除了指向 resource 的 pointer 外,還需要一個指向 RC 的 pointer。 2. **RC 的 memory 是動態配置的**:因為被指到的 resource 並不會知道 RC 的存在,所以無法事先 allocate memroy。 3. **必須以 atomic 的方式加減 RC**:因為可能有多個 thread 同時存取同一個 RC。 - 用 move 來 construct 另一個 shared_ptr 速度較快,因為不用存取 RC 的值。 - shared_ptr 也可以使用自訂的 deleter,且自訂的 deleter 並不會影響到 shared_ptr 的型別(和 unique_ptr 不同),參考如下: ```clike= auto custom_deleter = [](Base* base) { // sth to release... } shared_ptr<Base> spw(new Base, custom_deleter); ``` - shared_ptr<T> 內除了有 T 的 pointer 以外,還有一個 **Control Block** 的 pointer(所以前面才說 shared_ptr 是兩倍大,因為多存了一個 pointer),這個 control block 內部除了 RC 外、還有 **weak count**、一些資訊(e.g. 自訂的 deleter..等等),而這個 control block 是在 heap 上的(呼應前面第二點)。 - control block 是由第一個指向 object 的 shared_ptr 建立,以下規則一定會建立 control block: 1. std::make_shared:用 make_shared 一定會建立新的 object,所以也一併建立 control block。 2. 從 unique_ptr 來建立 shared_ptr:unique_ptr 沒有 control block,所以會順便建立 control block。 3. 用 raw pointer 來建立 shared_ptr:不建議用這種方式建立 shared_ptr。 - 考慮 code 如下: ```clike= auto pw = new Widget; // pw is a raw pointer. shared_ptr<Widget> spw1(pw, custom_deleter); // *pw has a control block. shared_ptr<Widget> spw2(pw, custom_deleter); // *pw has anothter control block. ``` 上述的 code 會讓 \*pw 有兩個 control block,所以在 spw1 解構時會清除 Widget,在 spw2 解構時又會再清除 Widget 一次,導致未定義行為。 可以修改成: ```clike= shared_ptr<Widget> spw1(new Widget, custom_deleter); shared_ptr<Widget> spw2(spw1); // spw2 has the same control block with spw1. ... // use make_shared if you don't have custom deleter. auto spw1 = make_shared<Widget>(); ``` - 另一個會造成多個 control block 的情況就是使用 this 的時候,參考 code 如下: ```clike= vector<shared_ptr<Widget>> process_widget; class Widget { // sth here... public: void process(); // sth here... } void Widget::process() { // sth to do... process_widget.emplace_back(this); } ``` 上述的 code 一樣也會讓 process_widget 內的 shared_ptr 為同一個 object 建立多個 control block。解決方法是讓 Widget 繼承 std::enable_shared_from_this,如下: ```clike= class Widget : public enable_shared_from_this<Widget> { // the same... } void Widget::process() { // sth to do... process_widget.emplace_back(shared_from_this()); } ``` 將原本傳入的 this 改為 shared_from_this(),shared_from_this() 會先尋找目前對應的 object 的 control block,如果存在的話再建立 shared_ptr。但是如果不存在該 control block,會拋出 exception。要避免這種情況,可以將 ctor 宣告為 private,再讓 user 透過 factory 取得 shared_ptr,例如: ```clike= class Widget : public enable_shared_from_this<Widget> { public: template<typename... Ts> // factory function. static shared_ptr<Widget> create(Ts&&... params); // the same... private: Widget() // private ctor. } ```
×
Sign in
Email
Password
Forgot password
or
By clicking below, you agree to our
terms of service
.
Sign in via Facebook
Sign in via Twitter
Sign in via GitHub
Sign in via Dropbox
Sign in with Wallet
Wallet (
)
Connect another wallet
New to HackMD?
Sign up