【Example】C++ 標準庫智慧指標 unique_prt 與 shared_ptr
【概念直接搬運Docs】C 樣式程式設計的一個主要 bug 型別是記憶體洩漏。 洩漏通常是由於為分配的記憶體的呼叫失敗引起的delete
new
。 現代 C++ 強調“資源獲取即初始化”(RAII) 原則。 其理念很簡單。 資源(堆記憶體、檔案控制代碼、套接字等)應由物件“擁有”。 該物件在其建構函式中建立或接收新分配的資源,並在其解構函式中將此資源刪除。 RAII 原則可確保當所屬物件超出範圍時,所有資源都能正確返回到作業系統。
為了支援對 RAII 原則的簡單採用,C++ 標準庫提供了三種智慧指標型別:std::unique_ptr、std::shared_ptr和std::weak_ptr
make_unique()
時在堆上分配的。 對和的new
呼叫delete
由unique_ptr
類封裝。 當widget
物件超出範圍時,將呼叫 unique_ptr 解構函式,此函式將釋放為陣列分配的記憶體。
在現代 c + + 程式設計中,標準庫包含智慧指標,這些指標用於幫助確保程式不會出現記憶體和資源洩漏,並具有異常安全。
====================================
unique_prt
unique_prt 型別智慧指標在設計上最顯著的特點是內部託管的指標一旦被建立就不能被任何形式的複製給另一個unique_prt,只可以被移動給另一個unique_prt。unique_prt 沒有拷貝建構函式,因此不能用於賦值。該指標最常用的情況是單例模式和編譯防火牆的封裝。
如果非要抬槓,使用 get() 函式獲取到裸指標給另外一個裸指標,那麼你使用智慧指標的意義又何在呢?
任何智慧指標都不應該去 get 裸指標使用,更不能 delete!
// 演示建立 unique_prt unique_ptr<Brain> u_brain = make_unique<Brain>(); u_brain->HelloWorld(); // 移動 unique_ptr unique_ptr<Brain> um_barin = std::move(u_brain); um_barin->HelloWorld();
// 移動方法2
std::swap(u_brain, um_brain);
// 錯誤 // um_barin = u_brain; // u_brain->HelloWorld(); // C26800
// 可以使用以下方法判斷是否為空指標 if (um_brain == nullptr) { std::cout << "um_brain is nullptr" << std::endl; }
// 可以釋放資源將指標恢復空指標
um_brain.reset();
====================================
shared_prt
和 unique 不同的是,它允許自身物件(shared_ptr)被複制,複製出來的 shared_ptr 所託管的指標都指向同一塊記憶體空間。而它的每一份拷貝(shared_ptr自身)都會有一個引用計數,資源的釋放由生命週期中最後一個 shared_ptr 負責。因此 shared_ptr 是最常用的智慧指標,也是最容易出問題的智慧指標。
使用它時應當注意:
1,不要將已存在裸指標交由 shared_ptr,任何形式的智慧指標都不應該去託管已有的裸指標。
2,作為函式引數傳遞時,請傳遞引用。因為作為值傳遞時,將產生大量無意義的引用計數。
3,共享所有權性質的物件往往比限定作用域的物件生存時間更久、資源開銷更大,尤其是多執行緒下。
// 建立 shared_ptr<Brain> s_brain = make_shared<Brain>(); s_brain->HelloWorld();
// 複製 shared_ptr<Brain> c_brain = s_brain; shared_ptr<Brain> d_brain = s_brain; // 檢查唯一性 std::cout << s_brain.unique() << std::endl; // 檢查引用計數數量 std::cout << s_brain.use_count() << std::endl;
與 shared_ptr 搭配的 weak_ptr
weak_ptr 設計上與 shared_ptr 搭配使用,因為 shared_ptr 存在一個問題,就是迴圈引用計數遞增而導致的記憶體洩漏:
比如說:
【虛擬碼】
class node{ shared_ptr<node> start; shared_ptr<node> end; } shared_ptr<node> nn = make; shared_ptr<node> mm = make; nn->end = mm; mm->start = nn;
這種情況,兩個node物件互相引用著對方,最終導致資源無法被釋放。所以有時候需要訪問 shared_ptr 自身,而不是它所託管的資源。
所以 weak_ptr 的作用就來了:
【虛擬碼】 class node{ weak_ptr<node> start; weak_ptr<node> end; } shared_ptr<node> nn = make; shared_ptr<node> mm = make; nn->end = mm; mm->start = nn;
為什麼呢?
因為 share_ptr 是強引用,強引用是隻要被引用的物件還存活那麼這個引用也一定會存在。
而 weak_ptr 是弱引用,弱引用是雖然物件還活著,但是這個引用則可有可無。
所以,weak_ptr 的作用就是作為一個 "觀察者" 訪問 shared_ptr 本身,而不是 shared_ptr 所託管的資源。
同時也意味著,weak_ptr 只能訪問它所觀察的shared_ptr 本身,而不能訪問 share_ptr 託管的資源,所以,它不會增加 shared_ptr 的引用計數。
shared_ptr<Brain> r_brain = make_shared<Brain>(); // 建立 weak_ptr weak_ptr<Brain> w_brain(r_brain); // 檢查 shared 引用計數數量 std::cout << w_brain.use_count() << std::endl; // 返回一個原始 shared 的拷貝(增加引用計數) shared_ptr<Brain> e_brain(w_brain.lock()); std::cout << w_brain.use_count() << std::endl;
====================================
make_unique 與 make_shared
這兩個標準庫函式是用於建立智慧指標的函式。
【以下懶得打字了直接抄的Docs,重點我劃出來】
使用make_shared
作為建立物件的簡單、更高效的方法,以及一個shared_ptr
來同時管理對物件的共享訪問。 在語義上,這兩個語句是等效的:
auto sp = std::shared_ptr<Example>(new Example(argument)); auto msp = std::make_shared<Example>(argument);
但是,第一條語句進行了兩個分配,如果在shared_ptr
物件的分配成功後,Example
的分配失敗,則未命名的Example
物件將被洩漏。 使用make_shared
的語句更簡單,因為只涉及到一個函式呼叫。 這樣會更有效,因為庫可能會對物件和智慧指標進行一個分配。 此函式的速度更快,導致記憶體碎片更少,但在一次分配時不存在異常,而不是在另一種分配上。 通過使引用物件和更新智慧指標中的引用計數的程式碼具有的更好的地址來提高效能。
make_unique如果不需要對物件的共享訪問許可權,請考慮使用。allocate_shared如果需要為物件指定自定義分配器,請使用。make_shared
如果物件需要自定義刪除器,則不能使用,因為無法將刪除器作為引數傳遞。
unique_ptr<Brain> u_brain = make_unique<Brain>();
shared_ptr<Brain> s_brain = make_shared<Brain>();
TRANSLATE with x
English
TRANSLATE with
EMBED THE SNIPPET BELOW IN YOUR SITE
Enable collaborative features and customize widget: Bing Webmaster Portal
Back
// 錯誤// um_barin = u_brain;// u_brain->HelloWorld(); // C26800