C++智能指针使用指南 part1:基本使用
加粗樣式>@TOC
智能指針是代理模式的具體應(yīng)用,它使用 RAII 技術(shù)代理了裸指針,能夠自動(dòng)釋放內(nèi)存, 無需程序員干預(yù),所以被稱為“智能指針”。
智能指針不是指針,而是一個(gè)對(duì)象,所以不要對(duì)其調(diào)用delete,它會(huì)自動(dòng)管理初始化時(shí)的指針,在離開作用域時(shí)析構(gòu)釋放內(nèi)存。
智能指針也沒有定義加減運(yùn)算,不能隨意移動(dòng)指針地址,這樣避免了指針越界操作。
在使用上:
如果指針是“獨(dú)占”使用,就應(yīng)該選擇 unique_ptr,它為裸指針添加了很多限制,更加安全 。
如果指針是“共享”使用,就應(yīng)該選擇 shared_ptr,它的功能非常完善,用法幾乎與原始指針一樣
使用智能指針要加頭文件#include <memory>
工廠函數(shù)make_unique()、make_shared()不只是返回智能指針對(duì)象,其內(nèi)部也有優(yōu)化。
如果你已經(jīng)理解了智能指針,就盡量不要再使用裸指針、new 和 delete 來操作內(nèi)存了。
unique_ptr
unique_ptr需要手動(dòng)初始化,聲明的時(shí)候必須用模板參數(shù)指定類型:
unique_ptr<int> ptr1(new int(10)); // int智能指針 assert(*ptr1 = 10); // 可以使用*取內(nèi)容 assert(ptr1 != nullptr); // 可以判斷是否為空指針 unique_ptr<string> ptr2(new string("hello")); // string智能指針 assert(*ptr2 == "hello"); // 可以使用*取內(nèi)容 assert(ptr2->size() == 5); // 可以使用->調(diào)用成員函數(shù)也可以調(diào)用工廠函數(shù),強(qiáng)制創(chuàng)建智能指針的時(shí)候必須初始化:
auto ptr3 = make_unique<int>(42); // 工廠函數(shù)創(chuàng)建智能指針 assert(ptr3 && *ptr3 == 42); auto ptr4 = make_unique<string>("god of war"); // 工廠函數(shù)創(chuàng)建智能指針 assert(!ptr4->empty());unique_ptr表示該智能指針的所有權(quán)是唯一的,不允許共享,任何時(shí)候只能有一個(gè)人持有。
它禁止拷貝賦值,但是可以使用std::move()顯式地聲明所有權(quán)轉(zhuǎn)移:
auto ptr1 = make_unique<int>(42); // 工廠函數(shù)創(chuàng)建智能指針 assert(ptr1 && *ptr1 == 42); // 此時(shí)智能指針有效 auto ptr2 = ptr1; // 編譯有問題 auto ptr2 = std::move(ptr1); // 使用move()轉(zhuǎn)移所有權(quán) assert(ptr2 && *ptr2 == 42); assert(ptr1); // 此時(shí)智能指針無效.會(huì)報(bào)錯(cuò)指針的所有權(quán)就被轉(zhuǎn)走了,原來的 unique_ptr 變成了空指針,新的 unique_ptr 接替了管理權(quán),保證所有權(quán)的唯一性
shared_ptr
基本使用方式與unique_ptr并無不同:
shared_ptr<int> ptr1(new int(10)); // int智能指針 assert(*ptr1 = 10); // 可以使用*取內(nèi)容 shared_ptr<string> ptr2(new string("hello")); // string智能指針 assert(*ptr2 == "hello"); // 可以使用*取內(nèi)容 auto ptr3 = make_shared<int>(42); // 工廠函數(shù)創(chuàng)建智能指針 assert(ptr3 && *ptr3 == 42); // 可以判斷是否為空指針 auto ptr4 = make_shared<string>("zelda"); // 工廠函數(shù)創(chuàng)建智能指針 assert(!ptr4->empty()); // 可以使用->調(diào)用成員函數(shù)不過它的所有權(quán)可以被安全共享,支持拷貝賦值
auto ptr1 = make_shared<int>(42); // 工廠函數(shù)創(chuàng)建智能指針 assert(ptr1 && ptr1.unique() ); // 此時(shí)智能指針有效且唯一 auto ptr2 = ptr1; // 直接拷貝賦值,不需要使用move() assert(ptr1 && ptr2); // 此時(shí)兩個(gè)智能指針均有效 assert(ptr1 == ptr2); // shared_ptr可以直接比較 // 兩個(gè)智能指針均不唯一,且引用計(jì)數(shù)為2 assert(!ptr1.unique() && ptr1.use_count() == 2); assert(!ptr2.unique() && ptr2.use_count() == 2);其內(nèi)部使用引用計(jì)數(shù),所以具有完整的”值語義“,可以在任何場(chǎng)合下代替原始指針。
不過維護(hù)引用計(jì)數(shù)的存儲(chǔ)和管理都是成本,過度使用會(huì)降低運(yùn)行效率。其引用計(jì)數(shù)也會(huì)帶來循環(huán)引用,下面是簡(jiǎn)化后的典型例子:
#include <iostream> #include <memory> #include <assert.h> using namespace std; class Node final { public:using this_type = Node;using shared_type = std::shared_ptr<this_type>;shared_type next; // 使用只能指針來指向下一個(gè)節(jié)點(diǎn) }; int main() {auto n1 = make_shared<Node>(); // 工廠函數(shù)創(chuàng)建智能指針auto n2 = make_shared<Node>();// 此時(shí)引用計(jì)數(shù)均為1assert(n1.use_count() == 1);assert(n2.use_count() == 1);// 產(chǎn)生循環(huán)引用n1->next = n2;n2->next = n1;// 此時(shí)引用計(jì)數(shù)均為2,且無法減到0,內(nèi)存泄露assert(n1.use_count() == 2);assert(n2.use_count() == 2); }這個(gè)例子很簡(jiǎn)單,你一下子就能看出存在循環(huán)引用。但在實(shí)際開發(fā)中,指針的關(guān)系可不像例 子那么清晰,很有可能會(huì)不知不覺形成一個(gè)鏈條很長(zhǎng)的循環(huán)引用,復(fù)雜到你根本無法識(shí)別, 想要找出來基本上是不可能的。 想要從根本上杜絕循環(huán)引用,光靠 shared_ptr 是不行了,必須要用到weak_ptr。
weak_ptr
它專門為打破循環(huán)引用而設(shè)計(jì),只觀察指針,不會(huì)增 加引用計(jì)數(shù)(弱引用),但在需要的時(shí)候,可以調(diào)用成員函數(shù) lock(),獲取 shared_ptr(強(qiáng)引用) 。用法如下
#include <iostream> #include <memory> #include <assert.h> using namespace std; class Node final { public:using this_type = Node;// 改用weak_ptrusing shared_type = std::weak_ptr<this_type>;shared_type next; // 使用只能指針來指向下一個(gè)節(jié)點(diǎn) }; int main() {auto n1 = make_shared<Node>(); // 工廠函數(shù)創(chuàng)建智能指針auto n2 = make_shared<Node>();// 此時(shí)引用計(jì)數(shù)均為1assert(n1.use_count() == 1);assert(n2.use_count() == 1);// 產(chǎn)生循環(huán)引用n1->next = n2;n2->next = n1;// 因?yàn)槭褂昧藈eak_ptr,引用計(jì)數(shù)為1assert(n1.use_count() == 1);assert(n2.use_count() == 1);if (!n1->next.expired()) { // 檢查指針是否有效auto ptr = n1->next.lock(); // lock()獲取shared_ptrassert(ptr == n2);} }總結(jié)
以上是生活随笔為你收集整理的C++智能指针使用指南 part1:基本使用的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 坎公骑冠剑浮游城工人怎么增加
- 下一篇: C++使用JSON的序列化与反序列化