Files
Obsidian-Main/01. 個人/02. 專注Study/C++/智慧指標.md
Awin Huang 0a8e11189d vault backup: 2022-09-26 18:49:43
Affected files:
.obsidian/daily-notes.json
.obsidian/plugins/periodic-notes/data.json
.obsidian/templates.json
.obsidian/workspace
01. 個人/02. 專注Study/Android/ADB 取得 APK 的 icon.md
01. 個人/02. 專注Study/Android/ADB.md
01. 個人/02. 專注Study/Android/AOSP.md
01. 個人/02. 專注Study/Android/Android programming.md
01. 個人/02. 專注Study/Android/Ktor.md
01. 個人/02. 專注Study/Android/Service.md
01. 個人/02. 專注Study/Android/Tools.md
01. 個人/02. 專注Study/Android/UI.md
01. 個人/02. 專注Study/C++/C++17.md
01. 個人/02. 專注Study/C++/Class template.md
01. 個人/02. 專注Study/C++/Structured binding declaration.md
01. 個人/02. 專注Study/C++/for_each.md
01. 個人/02. 專注Study/C++/lambda.md
01. 個人/02. 專注Study/C++/lvalue.md
01. 個人/02. 專注Study/C++/move operator.md
01. 個人/02. 專注Study/C++/rvalue.md
01. 個人/02. 專注Study/C++/智慧指標.md
01. 個人/02. 專注Study/RxKotlin/20200207 - Study RxKotlin.md
03. 資料收集/01. Programming/COM/20210726 - COM Interface.md
03. 資料收集/01. Programming/DB/MySQL.md
03. 資料收集/01. Programming/DB/sqlite.md
03. 資料收集/01. Programming/Design Pattern.md
03. 資料收集/01. Programming/FFMPEG/00. Introduction.md
03. 資料收集/01. Programming/FFMPEG/01. Setup.md
03. 資料收集/01. Programming/FFMPEG/FFMpeg.md
03. 資料收集/01. Programming/Flask.md
03. 資料收集/01. Programming/Media Foundation/20210604 - Windows media foundation.md
03. 資料收集/01. Programming/OpenCV.md
03. 資料收集/01. Programming/OpenGL.md
03. 資料收集/01. Programming/Python/argparse.ArgumentParser.md
03. 資料收集/01. Programming/Python/decorator.md
03. 資料收集/01. Programming/Python/logging.md
03. 資料收集/01. Programming/Python/opencv.md
03. 資料收集/01. Programming/Python/subprocess.md
03. 資料收集/01. Programming/Python/threading.md
03. 資料收集/01. Programming/Python/tkinter.md
03. 資料收集/01. Programming/Python/檢測工具.md
03. 資料收集/01. Programming/QT/Dropdown button.md
03. 資料收集/01. Programming/QT/QVariant.md
03. 資料收集/01. Programming/QT/Qt.md
03. 資料收集/01. Programming/UML.md
03. 資料收集/01. Programming/演算法.md
03. 資料收集/99. templates/blogHeader.md
03. 資料收集/99. templates/date.md
03. 資料收集/99. templates/front matter.md
03. 資料收集/99. templates/note.md
03. 資料收集/99. templates/table.md
03. 資料收集/99. templates/thisWeek.md
03. 資料收集/99. templates/日記.md
03. 資料收集/99. templates/讀書筆記.md
03. 資料收集/Hobby/RC.md
03. 資料收集/Hobby/RC/Traxxas Sledge.md
03. 資料收集/Hobby/RC/好盈電變調整中立點.md
03. 資料收集/Hobby/RC/差速器調教教學.md
03. 資料收集/Linux/CLI/cut.md
03. 資料收集/Linux/CLI/scp.md
03. 資料收集/Linux/CLI/timedatectl.md
03. 資料收集/Programming/Qt.md
03. 資料收集/Tool Setup/Hardware/RaspberryPi.md
03. 資料收集/Tool Setup/Software/Chrome.md
03. 資料收集/Tool Setup/Software/Obisidian.md
03. 資料收集/Tool Setup/Software/SublimeText.md
03. 資料收集/Tool Setup/Software/VirtualBox.md
03. 資料收集/Tool Setup/Software/Visual Studio Code.md
03. 資料收集/Tool Setup/Software/Windows Setup.md
03. 資料收集/Tool Setup/Software/Windows Terminal.md
03. 資料收集/Tool Setup/Software/freefilesync.md
03. 資料收集/Tool Setup/Software/vim.md
03. 資料收集/翻牆/V2Ray.md
03. 資料收集/翻牆/Wireguard.md
03. 資料收集/軟體工具/youtube-dl.md
2022-09-26 18:49:43 +08:00

5.0 KiB
Raw Blame History

unique_ptrshared_ptr都是智慧指標箱對於原本的raw pointer智慧指標使用起來更方便也不用擔心delete的問題。

unique_ptr

unique_ptr 的特點是,它保證在一個時間內,只會有一個指標的擁有者,也就是這個指標不能被複製跟移動,當 unique_ptr 離開它的scope時候它所擁有的指標也隨之被delete。這讓你不用擔心memory leak的問題。 假設我們有一個class叫 BigBuffer ,原本分配記憶體的方法:

BigBuffer* bigBuf = new BigBuffer(bufferSize);
// Use buffer here
delete bigBuf;

unique_ptr

auto bigBuf = std::make_unique<BigBuffer>(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() 重新配一個指標:

BigBuffer* pBuffer = new BigBuffer();
bigBuf.reset(pBuffer);

這時候舊指標會自動delete如果記憶體分配有成功的話bigBuf會接管剛剛new出來的指標或者變成 nullptr (記憶體分配失敗)。

如果單純想要釋放指標,那就單純的呼叫 reset() 就好。

bigBuf.reset(); // Now I'm nullptr

如果要分配陣列的話:

auto intArray = std::make_unique<int[]>(1024);

使用方式也是一樣的:

intArray[5] = 555;

不過對於陣列的操作更建議使用 std::array

如果有什麼特殊原因讓你決定不再讓 unique_ptr 來幫你管理指標,可以用 release() 來讓出指標:

auto intArray = std::make_unique<int[]>(1024);
int* intArrayRaw = intArray.release(); // Now I don't care anymore

但是這時候呼叫 delete[] (或 delete )的責任又回到你身上了。所以千萬不要把 release()reset() 搞混了。

unique_ptr 不能被複製跟移動,所以下列的寫法都編不過:

auto ptr1 = std::make_unique<int>(5);
std::unique_ptr<int> ptr2(ptr1);  // Error
std::unique_ptr<int> ptr2 = ptr1; // Error

在Visual Studio 2017上錯誤訊息是這樣error C2280: 'std::unique_ptr<int,std::default_delete<int>>::unique_ptr(const std::unique_ptr<int,std::default_delete<int>> &)': attempting to reference a deleted function。 其實就是unique_ptr的copy constructor跟assignment operator都被標記為delete了。

Move a unique_ptr

如果一定要把 unique_ptr 指定給別人可以嗎?可以的,用 std::move() 來轉移:

auto ptr1 = std::make_unique<int>(5);
// do something
auto anotherPtr = std::move(ptr1);

ptr1原本所管理的指標會轉移給 anotherPtrptr1 會變成 nullptr。

shared_ptr

建立一個 shared_ptr 是使用std::make_shared()

auto myBuf = std::make_shared<BigBuffer>(bufferSize);

但是 shared_ptr 可以被複製與移動,這是跟 unique_ptr 的差別:

auto myBuf = std::make_shared<BigBuffer>(bufferSize);

std::shared_ptr<BigBuffer> bufCopy = myBuf;

現在 bufCopy 跟 myBuf 都指向同一個指標,他們都可以操作這個指標:

myBuf->setZero(startAddr, endAddr);
bufCopy->setOne(startAddr, endAddr);

shared_ptr 內部有一個參考記數reference count來紀錄它所擁有的指標已經分享給幾個變數了只要有變數離開了他的scope參考記數就會減少反之要是像上面那樣有人複製指標參考記數就會增加參考記數歸0的時候指標就會被釋放。

有了 shared_ptr 我們就不必擔心 delete 的責任問題:

std::shared_ptr<BigBuffer> getBuffer(int32_t bufferSize) {
    return std::make_shared<BigBuffer>(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 還是讓記憶體的管理問題大大減少,應該用 shared_ptr 來代替 new & delete