vault backup: 2022-06-06 11:44:20

Affected files:
02. PARA/03. Resources(資源)/C++17/智慧指標.md
This commit is contained in:
2022-06-06 11:44:20 +08:00
parent 877b5a5b46
commit c5616acd70

View File

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