新聞中心
這篇文章主要介紹“C++智能指針shared_ptr與右值如何引用”的相關(guān)知識,小編通過實際案例向大家展示操作過程,操作方法簡單快捷,實用性強,希望這篇“C++智能指針shared_ptr與右值如何引用”文章能幫助大家解決問題。
10年積累的網(wǎng)站建設(shè)、網(wǎng)站設(shè)計經(jīng)驗,可以快速應(yīng)對客戶對網(wǎng)站的新想法和需求。提供各種問題對應(yīng)的解決方案。讓選擇我們的客戶得到更好、更有力的網(wǎng)絡(luò)服務(wù)。我雖然不認識你,你也不認識我。但先網(wǎng)站策劃后付款的網(wǎng)站建設(shè)流程,更有長樂免費網(wǎng)站建設(shè)讓你可以放心的選擇與我們合作。
1. 介紹
在 C++ 中沒有垃圾回收機制,必須自己釋放分配的內(nèi)存,否則就會造成內(nèi)存泄露。解決這個問題最有效的方法是使用智能指針(smart pointer)。智能指針是存儲指向動態(tài)分配(堆)對象指針的類,用于生存期的控制,能夠確保在離開指針所在作用域時,自動地銷毀動態(tài)分配的對象,防止內(nèi)存泄露。智能指針的核心實現(xiàn)技術(shù)是引用計數(shù),每使用它一次,內(nèi)部引用計數(shù)加1,每析構(gòu)一次內(nèi)部的引用計數(shù)減1,減為0時,刪除所指向的堆內(nèi)存。
C++11 中提供了三種智能指針,使用這些智能指針時需要引用頭文件:
std::shared_ptr:共享的智能指針
std::unique_ptr:獨占的智能指針
std::weak_ptr:弱引用的智能指針,它不共享指針,不能操作資源,是用來監(jiān)視 shared_ptr 的。
共享智能指針(shared_ptr)是指多個智能指針可以同時管理同一塊有效的內(nèi)存,共享智能指針 shared_ptr 是一個模板類,如果要進行初始化有三種方式:通過構(gòu)造函數(shù)、std::make_shared 輔助函數(shù)以及 reset 方法。共享智能指針對象初始化完畢之后就指向了要管理的那塊堆內(nèi)存,如果想要查看當前有多少個智能指針同時管理著這塊內(nèi)存可以使用共享智能指針提供的一個成員函數(shù) use_count
2. 初始化方法
2.1 通過構(gòu)造函數(shù)初始化
實例
// 使用智能指針管理一塊 int 型的堆內(nèi)存 shared_ptrptr1(new int(520));
2.2 通過拷貝和移動構(gòu)造函數(shù)初始化
調(diào)用拷貝構(gòu)造函數(shù)
shared_ptrptr2(ptr1);
調(diào)用移動構(gòu)造函數(shù)
std::shared_ptrptr5 = std::move(ptr2);
如果使用拷貝的方式初始化共享智能指針對象,這兩個對象會同時管理同一塊堆內(nèi)存,堆內(nèi)存對應(yīng)的引用計數(shù)也會增加;
如果使用移動的方式初始智能指針對象,只是轉(zhuǎn)讓了內(nèi)存的所有權(quán),管理內(nèi)存的對象并不會增加,因此內(nèi)存的引用計數(shù)不會變化。
2.2.1 移動構(gòu)造
關(guān)于移動構(gòu)造,可能有些讀者不太明白
移動構(gòu)造是C++11標準中提供的一種新的構(gòu)造方法。
在現(xiàn)實中有很多這樣的例子,我們將錢從一個賬號轉(zhuǎn)移到另一個賬號,將手機SIM卡轉(zhuǎn)移到另一臺手機,將文件從一個位置剪切到另一個位置……移動構(gòu)造可以減少不必要的復(fù)制,帶來性能上的提升。
我們首先來看看move函數(shù)
首先看這樣一段代碼
#include#include #include #include using namespace std; int main() { string st = "I love 進擊的汪sir"; vector vc; vc.push_back(move(st)); cout << vc[0] << endl; if (!st.empty()) cout << st << endl; return 0; }
輸出的結(jié)果為
再看這樣一段代碼
#include#include #include #include using namespace std; int main() { string st = "I love xing"; vector vc; vc.push_back(st); cout << vc[0] << endl; if (!st.empty()) cout << st << endl; return 0; }
其結(jié)果為
這兩段代碼唯一的不同是調(diào)用vc.push_back()將字符串插入到容器中去時,第一段代碼使用了move語句,而第二段代碼沒有使用move語句。輸出的結(jié)果差異也很明顯,第一段代碼中,原來的字符串st已經(jīng)為空,而第二段代碼中,原來的字符串st的內(nèi)容沒有變化。
先暫時記住這兩端代碼的輸出結(jié)果之間的差異。
我們回到移動構(gòu)造函數(shù)上
有時候我們會遇到這樣一種情況,我們用對象a初始化對象b,后對象a我們就不在使用了,但是對象a的空間還在呀(在析構(gòu)之前),既然拷貝構(gòu)造函數(shù),實際上就是把a對象的內(nèi)容復(fù)制一份到b中,那么為什么我們不能直接使用a的空間呢?這樣就避免了新的空間的分配,大大降低了構(gòu)造的成本。這就是移動構(gòu)造函數(shù)設(shè)計的初衷。
通俗一點的解釋就是,拷貝構(gòu)造函數(shù)中,對于指針,我們一定要采用深層復(fù)制,而移動構(gòu)造函數(shù)中,對于指針,我們采用淺層復(fù)制。
所以在上面的例子中,如果調(diào)用移動構(gòu)造函數(shù)來初始化智能指針,引用計數(shù)是不會增加的,而move函數(shù)實際上是返回的右值引用
2.2.2 右值引用
上面我們講到了右值引用,這里就來擴展一下右值引用是啥
首先得分清楚,什么是右值,什么是左值
lvalue 是 loactor value 的縮寫,rvalue 是 read value 的縮寫
左值是指存儲在內(nèi)存中、有明確存儲地址(可取地址)的數(shù)據(jù);
右值是指可以提供數(shù)據(jù)值的數(shù)據(jù)(不可取地址);
通過描述可以看出,區(qū)分左值與右值的便捷方法是:可以對表達式取地址(&)就是左值,否則為右值 。所有有名字的變量或?qū)ο蠖际亲笾担抑凳悄涿摹?/p>
C++11 中右值可以分為兩種:一個是將亡值( xvalue, expiring value),另一個則是純右值( prvalue, PureRvalue):
純右值:非引用返回的臨時變量、運算表達式產(chǎn)生的臨時變量、原始字面量和 lambda 表達式等
將亡值:與右值引用相關(guān)的表達式,比如,T&& 類型函數(shù)的返回值、 std::move 的返回值等。
右值引用就是對一個右值進行引用的類型。因為右值是匿名的,所以我們只能通過引用的方式找到它。無論聲明左值引用還是右值引用都必須立即進行初始化,因為引用類型本身并不擁有所綁定對象的內(nèi)存,只是該對象的一個別名。通過右值引用的聲明,該右值又“重獲新生”,其生命周期與右值引用類型變量的生命周期一樣,只要該變量還活著,該右值臨時量將會一直存活下去。
右值通過&&來引用
例如:
int&& value = 520;
里面 520 是純右值,value 是對字面量 520 這個右值的引用。int &&a2 = a1;
中 a1 雖然寫在了 = 右邊,但是它仍然是一個左值,使用左值初始化一個右值引用類型是不合法的。const Test& t = getObj()
這句代碼的語法是正確的,常量左值引用是一個萬能引用類型,它可以接受左值、右值、常量左值和常量右值。
2.3 通過 std::make_shared 初始化
通過 C++ 提供的 std::make_shared() 就可以完成內(nèi)存對象的創(chuàng)建并將其初始化給智能指針,函數(shù)原型如下:
template< class T, class... Args > shared_ptrmake_shared( Args&&... args );
實例
使用智能指針管理一塊 int 型的堆內(nèi)存, 內(nèi)部引用計數(shù)為 1
shared_ptrptr1 = make_shared (520);
注意
使用 std::make_shared() 模板函數(shù)可以完成內(nèi)存地址的創(chuàng)建,并將最終得到的內(nèi)存地址傳遞給共享智能指針對象管理。如果申請的內(nèi)存是普通類型,通過函數(shù)的()可完成地址的初始化,如果要創(chuàng)建一個類對象,函數(shù)的()內(nèi)部需要指定構(gòu)造對象需要的參數(shù),也就是類構(gòu)造函數(shù)的參數(shù)。
2.4 通過 reset 方法初始化
共享智能指針類提供的 std::shared_ptr::reset 方法函數(shù)原型如下:
void reset() noexcept; template< class Y > void reset( Y* ptr ); template< class Y, class Deleter > void reset( Y* ptr, Deleter d ); template< class Y, class Deleter, class Alloc > void reset( Y* ptr, Deleter d, Alloc alloc );
ptr:指向要取得所有權(quán)的對象的指針
d:指向要取得所有權(quán)的對象的指針
aloc:內(nèi)部存儲所用的分配器
實例
shared_ptrptr5; ptr5.reset(new int(250));
3. 獲取原始指針
對應(yīng)基礎(chǔ)數(shù)據(jù)類型來說,通過操作智能指針和操作智能指針管理的內(nèi)存效果是一樣的,可以直接完成數(shù)據(jù)的讀寫。但是如果共享智能指針管理的是一個對象,那么就需要取出原始內(nèi)存的地址再操作,可以調(diào)用共享智能指針類提供的 get () 方法得到原始地址,其函數(shù)原型如下:
T* get() const noexcept;
實例
#include#include #include using namespace std; int main() { int len = 128; shared_ptr ptr(new char[len]); // 得到指針的原始地址 char* add = ptr.get(); memset(add, 0, len); strcpy(add, "博客:進擊的汪sir"); cout << "string: " << add << endl; shared_ptr p(new int); *p = 100; cout << *p.get() << " " << *p << endl; return 0; }
4. 指定刪除器
當智能指針管理的內(nèi)存對應(yīng)的引用計數(shù)變?yōu)?0 的時候,這塊內(nèi)存就會被智能指針析構(gòu)掉了。另外,我們在初始化智能指針的時候也可以自己指定刪除動作,這個刪除操作對應(yīng)的函數(shù)被稱之為刪除器,這個刪除器函數(shù)本質(zhì)是一個回調(diào)函數(shù),我們只需要進行實現(xiàn),其調(diào)用是由智能指針完成的。
實例
#include#include using namespace std; // 自定義刪除器函數(shù),釋放int型內(nèi)存 void deleteIntPtr(int* p) { delete p; cout << "int 型內(nèi)存被釋放了..."; } int main() { shared_ptr ptr(new int(250), deleteIntPtr); return 0; }
刪除器函數(shù)也可以是 lambda 表達式!
關(guān)于“C++智能指針shared_ptr與右值如何引用”的內(nèi)容就介紹到這里了,感謝大家的閱讀。如果想了解更多行業(yè)相關(guān)的知識,可以關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,小編每天都會為大家更新不同的知識點。
本文題目:C++智能指針shared_ptr與右值如何引用
分享地址:http://www.ef60e0e.cn/article/pesogj.html