026 weak_ptr

weak_ptr 是一種不控制所指向?qū)ο笊嫫诘闹悄苤羔槪赶蛴梢粋€(gè) shared_ptr 管理的對(duì)象,將一個(gè) weak_ptr 綁定到一個(gè) shared_ptr 不會(huì)改變 shared_ptr 的引用計(jì)數(shù),一旦最后一個(gè)指向?qū)ο蟮?shared_ptr 被銷毀,對(duì)象就會(huì)被釋放。即使有 weak_ptr 指向?qū)ο螅瑢?duì)象也還是會(huì)被釋放,因此, weak_ptr 的名字抓住了這種智能指針“弱”共享對(duì)象的特點(diǎn)。

weak_ptr 說明
weak_ptr<T> w 空 weak_ptr 可以指向類型為 T 的對(duì)象
weak_ptr<T> w(sp) 與 shared_ptr sp 指向相同對(duì)象的 weak_ptr,T 必須能轉(zhuǎn)換為 sp 指向的類型
w = p p 可以是一個(gè) shared_ptr 或一個(gè) weak_ptr,賦值后 w 與 p 共享對(duì)象
w.reset() 將 w 置為空
w.use_count() 與 w 共享對(duì)象的 shared_ptr 的數(shù)量
w.expired() 若 w.use_count() 為 0,返回 true,否則返回 false
w.lock() 如果 expired 為 true,返回一個(gè)空 shared_ptr;否則返回一個(gè)指向 w 的對(duì)象的 shared_ptr

當(dāng)我們創(chuàng)建一個(gè) weak_ptr 時(shí),要用一個(gè) shared_ptr 來初始化它:

auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp 弱共享 p;p 的引用計(jì)數(shù)未改變

本例中 wp 和 p 指向相同的對(duì)象,由于是弱共享,創(chuàng)建 wp 不會(huì)改變 p 的引用計(jì)數(shù);wp 指向的對(duì)象可能被釋放掉。

由于對(duì)象可能不存在,我們不能使用 weak_ptr 直接訪問對(duì)象,而必須調(diào)用 lock。此函數(shù)檢查 weak_ptr 指向的對(duì)象是否仍存在,如果存在,lock 返回一個(gè)指向共享對(duì)象的 shared_ptr。與任何其他 shared_ptr 類似,只要此 shared_ptr 存在,它所指向的底層對(duì)象也就會(huì)一直存在。例如:

if (shared_ptr<int> np = wp.lock()) { // 如果 np 不為空則條件成立
    // 在 if 中,np 與 p 共享對(duì)象
}

在這段代碼中,只有當(dāng) lock 調(diào)用返回 true 時(shí)我們才會(huì)進(jìn)入 if 語句體。在 if 中,使用 np 訪問共享對(duì)象是安全的。

核查指針類

作為 weak_ptr 用途的一個(gè)展示,我們將為 StrBlob 類定義一個(gè)伴隨指針類。我們的指針類將命名為 StrBlobPtr,會(huì)保存一個(gè) weak_ptr,指向 StrBlob 的 data 成員,這是初始化時(shí)提供給它的,通過使用 weak_ptr,不會(huì)影響一個(gè)給定的 StrBlob 所指向的 vector 的生存期。但是,可以阻止用戶訪問一個(gè)不再存在的 vector 的企圖。

StrBlobPtr 會(huì)有兩個(gè)數(shù)據(jù)成員:wptr,或者為空,或者指向一個(gè) StrBlob 中的 vector;curr,保存當(dāng)前對(duì)象所表示的元素的下標(biāo),類似它的伴隨類 StrBlob,我們的指針類也有一個(gè) check 成員來檢查解引用 StrBlobPtr 是否安全:

// 對(duì)于訪問一個(gè)不存在元素的嘗試,StrBlobPtr 拋出一個(gè)異常
class StrBlobPtr {
public:
    StrBlobPtr(): curr(0) {}
    StrBlobPtr(StrBlob &a, size_t sz = 0): 
            wptr(a.data), curr(sz) {}
    std::string & deref() const;
    StrBlobPtr& incr(); // 前綴遞增
private:
    // 若檢查成功,check 返回一個(gè)指向 vector 的 shared_ptr
    std::shared_ptr<std::vector<std::string>>
            check(std::size_t, const std::string& ) const;
    // 保存一個(gè) weak_ptr,意味看底層 vector 可能會(huì)被銷毀
    std::weak_ptr<std::vector<std::string>> wptr;
    std:size_t curr; // 在數(shù)組中的當(dāng)前位置
}

默認(rèn)構(gòu)造函數(shù)生成一個(gè)空的 StrBlobPtr,其構(gòu)造函數(shù)初始化列表將 curr 顯式初始化為 0,并將 wptr 隱式初始化為一個(gè)空 weak_ptr,第二個(gè)構(gòu)造函數(shù)接受一個(gè) StrBlob 引用和一個(gè)可選的索引值,此構(gòu)造函數(shù)初始化 wptr,令其指向給定 StrBlob 對(duì)象的 shared_ptr 中的 vector,并將 curr 初始化為 sz 的值。我們使用了默認(rèn)參數(shù),表示默認(rèn)情況下將 curr 初始化為第一個(gè)元素的下標(biāo)。我們將會(huì)看到,StrBlob 的 end 成員將會(huì)用到參數(shù) sz。

值得注意的是,我們不能將 StrBlobPtr 綁定到一個(gè) const StrBlob 對(duì)象。這個(gè)限制是由于構(gòu)造函數(shù)接受一個(gè)非 const StrBlob 對(duì)象的引用而導(dǎo)致的。

StrBlobPtr 的 check 成員與 StrBlob 中的同名成員不同,它還要檢查指針指向的 vector 是否還存在:

std::shared_ptr<std::vector<std::string>>
StrBlobPtr::check(std::size_t i, const std::string &msg) const {
    auto ret = wptr.lock(); // vector 還存在嗎?
    if (!ret){
        throw std::runtime_error("unbound StrBlobPtr");
    }
    if (i >= ret->size()){
        throw  std::out_of_range(msg);
    }
    return ret; // 否則,返回指向 vector 的 shared_ptr
}

由于一個(gè) weak_ptr 不參與其對(duì)應(yīng)的 shared_ptr 的引用計(jì)數(shù),StrBlobPtr 指向的 vector 可能已經(jīng)被釋放了。如果 vector 已銷毀,lock 將返回一個(gè)空指針。在本例中,任何 vector 的引用都會(huì)失敗,于是拋出一個(gè)異常。否則,check 會(huì)檢查給定索引,如果索引值合法,check 返回從 lock 獲得的 shared_ptr。

指針操作

我們將定義名為 deref 和 incr 的函數(shù),分別用來解引用和遞增 StrBlobPtr。

deref 成員調(diào)用 check,檢查使用 vector 是否安全以及 curr 是否在合法范圍內(nèi):

std::string& StrBlobPtr::deref() const {
    auto p = check(curr, "dereference past end");
    return (*p)[curr]; //(*p) 是對(duì)象所指向的 vector
}

如果 check 成功,p 就是一個(gè) shared_ptr,指向 StrBlobPtr 所指向的 vector。表達(dá)式 (*p)[curr] 解引用 shared_ptr 來獲得 vector,然后使用下標(biāo)運(yùn)算符提取并返回 curr 位置上的元素。

incr 成員也調(diào)用 check:

// 前綴遞增:返回遞增后的對(duì)象的引用
StrBlobPtr& StrBlobPtr::incr() {
    // 如果 curr 已經(jīng)指向容器的尾后位置,就不能遞增它
    check(curr, "increment past end of StrBlobPtr");
    ++curr; // 推進(jìn)當(dāng)前位置
    return *this;
}

當(dāng)然,為了訪問 data 成員,我們的指針類必須聲明為 StrBlob 的 friend 我們還要為 StrBlob 類定義 begin 和 end 操作,返回一個(gè)指向它自身的 StrBlobPtr:

// 對(duì)于 StrBlob 中的友元聲明來說,此前置聲明是必要的
class StrBlobPtr;
class StrBlob {
    friend class StrBlobPtr;
    // 其他成員
    // 返回指向首元素和尾后元素的 StrBlobPtr
    StrBlobPtr begin() {return StrBlobPtr(*this); }
    StrBlobPtr end() {
        auto ret = StrBlobPtr(*this, data->size());
        return ret;
    }
};
?著作權(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)容

  • 導(dǎo)語: C++指針的內(nèi)存管理相信是大部分C++入門程序員的夢(mèng)魘,受到Boost的啟發(fā),C++11標(biāo)準(zhǔn)推出了智能指針...
    7ee72f98ad17閱讀 1,028評(píng)論 0 1
  • 12.1 智能指針 智能指針行為類似普通指針,但它負(fù)責(zé)自動(dòng)釋放所知的對(duì)象。 #include <memory> s...
    龍遁流閱讀 420評(píng)論 0 1
  • 原作者:Babu_Abdulsalam 本文翻譯自CodeProject,轉(zhuǎn)載請(qǐng)注明出處。 引入### Ooops...
    卡巴拉的樹閱讀 30,330評(píng)論 13 74
  • C++裸指針的內(nèi)存問題有:1、空懸指針/野指針2、重復(fù)釋放3、內(nèi)存泄漏4、不配對(duì)的申請(qǐng)與釋放 使用智能指針可以有效...
    WalkeR_ZG閱讀 3,278評(píng)論 0 5
  • shared_ptr 類 類似 vector,智能指針也是模板。因此,當(dāng)我們創(chuàng)建一個(gè)智能指針時(shí),必須提供額外的信息...
    趙者也閱讀 908評(píng)論 0 0

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