unique_ptr與shared_ptr都是智慧指標,箱對於原本的raw pointer,智慧指標使用起來更方便,也不用擔心delete的問題。 ## unique_ptr `unique_ptr`的特點是,它保證在一個時間內,只會有一個指標的擁有者,也就是這個指標不能被複製跟移動,當`unique_ptr`離開它的scope時候,它所擁有的pointer也隨之被delete。這讓你不用擔心memory leak的問題。 原本分配記憶體的方法,假設我們有一個class叫`BigBuffer`: ```cpp BigBuffer* bigBuf = new BigBuffer(bufferSize); // Use buffer here delete bigBuf; ``` 用`unique_ptr`: ```cpp auto bigBuf = std::make_unique(bufferSize); // Use buffer here // bigBuf will be released when exiting scope ``` 我們統一用`std::make_unique<>`這個template function來建立`unique_ptr`,角括號`<>`裡面要帶入你要建立的型別,後面的括號`()`就是型別的constructor,使用起來跟`new`是一樣的。 因為`std::make_unique<>`裡面已經有表明型別了,所以變數就用`auto`就可以了,不用再寫一次型別。 一旦`unique_ptr`建立之後,使用起來就跟一般指標沒有兩樣,都是用`->`來操作:`bigBuf->setXXX()` or `bigBuf->getXXX()`。 但是別忘記`unique_ptr`本身還是一個local variable,所以我們可以用`.`來操作`unique_ptr`,例如我們可以用`.reset()`重新配一個指標: ```cpp bigBuf.reset(std::make_unique(bufferSizeLarger)); ``` 這時候舊指標會自動delete,然後指向新的指標(如果記憶體分配有成功的話),或者指向`nullptr`(記憶體分配失敗)。 如果單純想要釋放指標,那就單純的呼叫`reset()`就好。 ```cpp bigBuf.reset(); // Now I'm nullptr ``` 如果要分配陣列的話: ```cpp auto intArray = std::make_unique(1024); ``` 使用方式也是一樣的: ```cpp intArray[5] = 555; ``` 不過對於陣列的操作現在更建議使用`std::array`。 如果有什麼特殊原因讓你決定不再讓`unique_ptr`來幫你管理指標,可以用`release()`來讓出指標: ```cpp auto intArray = std::make_unique(1024); int* intArrayRaw = intArray.release(); // Now I don't care anymore ``` 但是這時候呼叫`delete[]`(或`delete`)的責任又回到你身上了。所以千萬不要把`release()`跟`reset()`搞混了。 `unique_ptr`不能被複製跟移動,所以下列的寫法都編不過: ```cpp auto ptr1 = std::make_unique(5); std::unique_ptr ptr2(ptr1); // Error std::unique_ptr ptr2 = ptr1; // Error ``` 在Visual Studio 2017上,錯誤訊息是這樣:`error C2280: 'std::unique_ptr>::unique_ptr(const std::unique_ptr> &)': attempting to reference a deleted function`。 其實就是`unique_ptr`的copy constructor跟assignment operator都被標記為delete了。 ## shared_ptr 建立一個`shared_ptr`是使用`std::make_shared()`: ```cpp auto myBuf = std::make_shared(bufferSize); ``` 但是`shared_ptr`可以被複製與移動,這是跟`unique_ptr`的差別: ```cpp auto myBuf = std::make_shared(bufferSize); std::shared_ptr bufCopy = myBuf; ``` 現在bufCopy跟myBuf都指向同一個指標,他們都可以操作這個指標: ```cpp myBuf->setZero(startAddr, endAddr); bufCopy->setOne(startAddr, endAddr); ``` `shared_ptr`內部有一個參考記數(reference count)來紀錄它所擁有的指標已經分享給幾個變數了,只要有變數離開了他的scope,參考記數就會減少,反之,要是像上面那樣有人複製的指標,參考記數就會增加,參考記數歸0的時候,指標就會被釋放。 有了`shared_ptr`我們就不必擔心delete的責任問題: ```cpp std::shared_ptr getBuffer(int32_t bufferSize) { return std::make_shared(bufferSize); } int main() { auto myBuf = getBuffer(1024); // new(malloc) memory // use myBuf return 0; } // myBuf delete memory here ``` `shared_ptr`有一個問題是可以會「循環參考」(cyclic references),也就是share_ptr A指向另一個share_ptr B,然後share_ptr B又指向share_ptr A,這造成參考記數(reference count)不會減少而永遠無法釋出指標。這個是需要注意的。 但是`shared_ptr`還是讓記憶體的ㄈ