025 unique_ptr

一個(gè) unique_ptr “擁有” 它所指向的對(duì)象。與 shared_ptr 不同,某個(gè)時(shí)刻只能有一個(gè) unique_ptr 指向一個(gè)給定對(duì)象,當(dāng) unique_ptr 被銷毀時(shí),它所指向的對(duì)象也被銷毀。

與 shared_ptr 不同,沒有類似 make_shared 的標(biāo)準(zhǔn)庫(kù)函數(shù)返回一個(gè) unique_ptr,當(dāng)我們定義一個(gè) unique_ptr 時(shí),需要將其綁定到一個(gè) new 返回的指針上。類似 shared_ptr,初始化 unique_ptr 必須采用直接初始化形式:

unique_ptr<double> p1; // 可以指向一個(gè) double 的 unique_ptr
unique_ptr<int> p2(new int(42)); // p2 指向一個(gè)值為 42 的 int

由于一個(gè) unique_ptr 擁有它指向的對(duì)象,因此 unique_ptr 不支持普通的拷貝或賦值操作:

unique_ptr<string> p1(new string("Stegosaurus"));
unique_ptr<string> p2(p1); // 錯(cuò)誤:unique_ptr 不支持拷貝
unique_ptr<string> p3;
p3=p2; // 錯(cuò)誤:unique_ptr不支持賦值

shared_ptr 和 unique_ptr 都支持的操作

操作 說明
shared_ptr<T> sp
unique_ptr<T> up
空智能指針,可以指向類型為 T 的對(duì)象
p 將 p 用作一個(gè)條件判斷,若 p 指向一個(gè)對(duì)象,則為 true
*p 解引用 p,獲得它指向的對(duì)象
p->mem 等價(jià)于(*p).mem
p.get() 返回 p 中保存的指針。要小心使用,若智能指針釋放了其對(duì)象,返回的指針?biāo)赶虻膶?duì)象也就消失了
swap(p, q)
p.swap(q)
交換 p 和 q 中的指針

只適用于 unique_ptr 的操作

操作 說明
unique_ptr<T> u1 空 unique_ptr,可以指向類型為 T 的對(duì)象 u1 會(huì)使用 delete 來釋放它的指針
unique_ptr<T, D> u2 空 unique_ptr,可以指向類型為 T 的對(duì)象 u2 會(huì)使用一個(gè)類型為 D 的可調(diào)用對(duì)象來釋放它的指針
unique_ptr<T, D> u(d) 空 unique_ptr,指向類型為 T 的對(duì)象,用類型為 D 的對(duì)象 d 代替 de1ete
u = nullptr 釋放 u 指向的對(duì)象,將 u 置為空
u.release() u 放棄對(duì)指針的控制權(quán),返回指針,并將 u 置為空
u.reset() 釋放 u 指向的對(duì)象
u.reset(q)
u.reset(nullptr)
如果提供了內(nèi)置指針 q,令 u 指向這個(gè)對(duì)象;否則將 u 置為空

雖然我們不能拷貝或賦值 unique_ptr,但可以通過調(diào)用 release 或 reset 將指針的所有權(quán)從一個(gè)(非 const)unique_ptr 轉(zhuǎn)移給另一個(gè) unique:

// 將所有權(quán)從 p1(指向 string Stegosaurus)轉(zhuǎn)移給 p2
unique_ptr<string> p2(p1.release()); //  release 將 p1 置為空
unique_ptr<string> p3(new string("Trex"));
// 將所有權(quán)從 p3 轉(zhuǎn)移給 p2
p2.reset(p3, release()); // reset 釋放了 p2 原來指向的內(nèi)存

release 成員返回 unique_ptr 當(dāng)前保存的指針并將其置為空。因此,p2 被初始化為 p1 原來保存的指針,而 p1 被置為空。

reset 成員接受一個(gè)可選的指針參數(shù),令 unique_ptr 重新指向給定的指針,如果 unique_ptr 不為空,它原來指向的對(duì)象被釋放。因此,對(duì) p2 調(diào)用 reset 釋放了用 “Stegosaurus” 初始化的 string 所使用的內(nèi)存,將 p3 對(duì)指針的所有權(quán)轉(zhuǎn)移給 p2,并將 p3 置為空。

調(diào)用 release 會(huì)切斷 unique_ptr 和它原來管理的對(duì)象間的聯(lián)系。release 返回的指針通常被用來初始化另一個(gè)智能指針或給另一個(gè)智能指針賦值。在本例中,管理內(nèi)存的責(zé)任簡(jiǎn)單地從一個(gè)智能指針轉(zhuǎn)移給另一個(gè),但是,如果我們不用另一個(gè)智能指針來保存 release 返回的指針,我們的程序就要負(fù)責(zé)資源的釋放:

p2.release(); // 錯(cuò)誤:p2 不會(huì)釋放內(nèi)存,而且我們丟失了指針
auto p = p2.release(); // 正確,但我們必須記得 delete(p)

傳遞 unique_ptr 參數(shù)和返回 unique_ptr

不能拷貝 unique_ptr 的規(guī)則有一個(gè)例外:我們可以拷貝或賦值一個(gè)將要被銷毀的 unique_ptr,最常見的例子是從函數(shù)返回一個(gè) unique_ptr:

unique_ptr<int> clone(int p) {
    // 正確:從 int* 創(chuàng)建一個(gè) unique_ptr<int>
    return unique_ptr<int>(new int(p));
}

還可以返回一個(gè)局部對(duì)象的拷貝:

unique_ptr<int> clone(int p) {
    unique_ptr<int> ret(new int(p));
    // ...
    return ret;
}

對(duì)于兩段代碼,編譯器都知道要返回的對(duì)象將要被銷毀。

向后兼容:auto_ptr

標(biāo)準(zhǔn)庫(kù)的較早版本包含了一個(gè)名為 auto_ptr 的類,它具有unique_ptr 的部分特性,但不是全部。特別是,我們不能在容器中保存 auto_ptr,也不能從函數(shù)中返回 auto_ptr。雖然 auto_ptr 仍是標(biāo)準(zhǔn)庫(kù)的一部分,但編寫程序時(shí)應(yīng)該使用 unique_ptr。

向 unique_ptr 傳遞刪除器

類似 shared_ptr,unique_ptr 默認(rèn)情況下用 delete 釋放它指向的對(duì)象,與 shared_ptr 一樣,我們可以重載一個(gè) unique_ptr 中默認(rèn)的刪除器。但是,unique_ptr 管理刪除器的方式與 shared_ptr 不同。

重載一個(gè) unique_ptr 中的刪除器會(huì)影響到 unique_ptr 類型以及如何構(gòu)造(或 reset)該類型的對(duì)象。與重載關(guān)聯(lián)容器的比較操作類似我們必須在尖括號(hào)中 unique_ptr 指向類型之后提供刪除器類型。在創(chuàng)建或 reset 一個(gè)這種 unique_ptr 類型的對(duì)象時(shí),必須提供一個(gè)指定類型的可調(diào)用對(duì)象(刪除器):

// p 指向一個(gè)類型為 objT 的對(duì)象,開使用一個(gè)類型為 delT 的對(duì)象釋放 objT 對(duì)象
// 它會(huì)調(diào)用一個(gè)名為 fcn 的 delT 類型對(duì)象
unique_ptr<objT, delT> p(new objT, fcn);

作為一個(gè)更具體的例子,我們將重寫連接程序,用 unique_ptr 米代替 shared_ptr 如下所示:

void f(destination &d /* 其他需要的參數(shù) */) {
    connection c = connect(&d); // 打開連接
    // 當(dāng) p 被銷毀時(shí),連接將會(huì)關(guān)閉
    unique_ptr<connection, decltype(end_connection)*> p(&c, end_connection):
    // 使用連接
    // 當(dāng) f 退出時(shí)(即使是由于異常而退出),connection 會(huì)被正確關(guān)閉
}

在本例中我們使用了 decltype 來指明函數(shù)指針類型。由于 decltype(end_connection)返回一個(gè)函數(shù)類型,所以我們必須添加一個(gè) * 來指出我們正在使用該類型的一個(gè)指針。

unique_ptr 和動(dòng)態(tài)數(shù)組

標(biāo)準(zhǔn)庫(kù)提供了一個(gè)可以管理 new 分配的數(shù)組的 unique_ptr 版本。為了用一個(gè) unique_ptr 管理動(dòng)態(tài)數(shù)組,我們必須在對(duì)象類型后面跟一對(duì)空方括號(hào):

// up 指向一個(gè)包含 10 個(gè)未初始化 int 的數(shù)組
unique_ptr<int[]> up(new int[10]);
up.release(); // 自動(dòng)用 delete[] 銷毀其指針

類型說明符中的方括號(hào)(<int[]>)指出 up 指向一個(gè) int 數(shù)組而不是一個(gè) int。由于 up 指向一個(gè)數(shù)組,當(dāng) up 銷毀它管理的指針時(shí),會(huì)自動(dòng)使用 delete[]。

指向數(shù)組的 unique_ptr 提供的操作與我們?cè)谥惺褂玫哪切┎僮饔幸恍┎煌?。?dāng)一個(gè) unique_ptr 指向一個(gè)數(shù)組時(shí),我們不能使用點(diǎn)和箭頭成員運(yùn)算符。畢竟 unique_ptr 指向的是一個(gè)數(shù)組而不是單個(gè)對(duì)象,因此這些運(yùn)算符是無(wú)意義的。另一方面,當(dāng)一個(gè) unique_ptr 指向一個(gè)數(shù)組時(shí)我們可以使用下標(biāo)運(yùn)算符來訪問數(shù)組中的元素:

for (size_t i = 0; i != 10; ++i) {
    up[i] = i; // 為每個(gè)元素賦予一個(gè)新值
}
指向數(shù)組的 unique_ptr 說明
指向數(shù)組的 unique_ptr 不支持成員訪問運(yùn)算符(點(diǎn)和箭頭運(yùn)算符) 其他 unique_ptr 操作不變
unique_ptr<T[]> u u 可以指向一個(gè)動(dòng)態(tài)分配的數(shù)組,數(shù)組元素類型為 T
unique_ptr<T[]> u(p) u 指向內(nèi)置指針 p 所指向的動(dòng)態(tài)分配的數(shù)組。p 必須能轉(zhuǎn)換為類型 T*
u[i] u 必須指向一個(gè)數(shù)組,返回 u 擁有的數(shù)組中位置 i 處的對(duì)象
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • shared_ptr 類 類似 vector,智能指針也是模板。因此,當(dāng)我們創(chuàng)建一個(gè)智能指針時(shí),必須提供額外的信息...
    趙者也閱讀 908評(píng)論 0 0
  • 12.1 智能指針 智能指針行為類似普通指針,但它負(fù)責(zé)自動(dòng)釋放所知的對(duì)象。 #include <memory> s...
    龍遁流閱讀 420評(píng)論 0 1
  • C++裸指針的內(nèi)存問題有:1、空懸指針/野指針2、重復(fù)釋放3、內(nèi)存泄漏4、不配對(duì)的申請(qǐng)與釋放 使用智能指針可以有效...
    WalkeR_ZG閱讀 3,278評(píng)論 0 5
  • 總結(jié) unique_ptr指針的一些特性總結(jié) 默認(rèn)情況下,占用的內(nèi)存大小和raw指針一樣。(除非指定了用戶自定義d...
    EVANMORE閱讀 1,188評(píng)論 0 2
  • 導(dǎo)語(yǔ): C++指針的內(nèi)存管理相信是大部分C++入門程序員的夢(mèng)魘,受到Boost的啟發(fā),C++11標(biāo)準(zhǔn)推出了智能指針...
    7ee72f98ad17閱讀 1,028評(píng)論 0 1

友情鏈接更多精彩內(nèi)容