weak_ptr 是一種不控制所指向?qū)ο笊嫫诘闹悄苤羔樕系矗赶蛴梢粋€ shared_ptr 管理的對象诗箍,將一個 weak_ptr 綁定到一個 shared_ptr 不會改變 shared_ptr 的引用計數(shù)牵敷,一旦最后一個指向?qū)ο蟮?shared_ptr 被銷毀额港,對象就會被釋放褐鸥。即使有 weak_ptr 指向?qū)ο笱恚瑢ο笠策€是會被釋放蝶棋,因此, weak_ptr 的名字抓住了這種智能指針“弱”共享對象的特點卸亮。
weak_ptr | 說明 |
---|---|
weak_ptr<T> w | 空 weak_ptr 可以指向類型為 T 的對象 |
weak_ptr<T> w(sp) | 與 shared_ptr sp 指向相同對象的 weak_ptr,T 必須能轉(zhuǎn)換為 sp 指向的類型 |
w = p | p 可以是一個 shared_ptr 或一個 weak_ptr玩裙,賦值后 w 與 p 共享對象 |
w.reset() | 將 w 置為空 |
w.use_count() | 與 w 共享對象的 shared_ptr 的數(shù)量 |
w.expired() | 若 w.use_count() 為 0兼贸,返回 true,否則返回 false |
w.lock() | 如果 expired 為 true吃溅,返回一個空 shared_ptr溶诞;否則返回一個指向 w 的對象的 shared_ptr |
當我們創(chuàng)建一個 weak_ptr 時,要用一個 shared_ptr 來初始化它:
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp 弱共享 p决侈;p 的引用計數(shù)未改變
本例中 wp 和 p 指向相同的對象螺垢,由于是弱共享,創(chuàng)建 wp 不會改變 p 的引用計數(shù)赖歌;wp 指向的對象可能被釋放掉枉圃。
由于對象可能不存在,我們不能使用 weak_ptr 直接訪問對象庐冯,而必須調(diào)用 lock孽亲。此函數(shù)檢查 weak_ptr 指向的對象是否仍存在,如果存在展父,lock 返回一個指向共享對象的 shared_ptr返劲。與任何其他 shared_ptr 類似,只要此 shared_ptr 存在栖茉,它所指向的底層對象也就會一直存在篮绿。例如:
if (shared_ptr<int> np = wp.lock()) { // 如果 np 不為空則條件成立
// 在 if 中,np 與 p 共享對象
}
在這段代碼中,只有當 lock 調(diào)用返回 true 時我們才會進入 if 語句體吕漂。在 if 中亲配,使用 np 訪問共享對象是安全的。
核查指針類
作為 weak_ptr 用途的一個展示,我們將為 StrBlob 類定義一個伴隨指針類弃榨。我們的指針類將命名為 StrBlobPtr菩收,會保存一個 weak_ptr梨睁,指向 StrBlob 的 data 成員鲸睛,這是初始化時提供給它的,通過使用 weak_ptr坡贺,不會影響一個給定的 StrBlob 所指向的 vector 的生存期官辈。但是,可以阻止用戶訪問一個不再存在的 vector 的企圖遍坟。
StrBlobPtr 會有兩個數(shù)據(jù)成員:wptr拳亿,或者為空,或者指向一個 StrBlob 中的 vector愿伴;curr肺魁,保存當前對象所表示的元素的下標,類似它的伴隨類 StrBlob隔节,我們的指針類也有一個 check 成員來檢查解引用 StrBlobPtr 是否安全:
// 對于訪問一個不存在元素的嘗試鹅经,StrBlobPtr 拋出一個異常
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 返回一個指向 vector 的 shared_ptr
std::shared_ptr<std::vector<std::string>>
check(std::size_t, const std::string& ) const;
// 保存一個 weak_ptr怎诫,意味看底層 vector 可能會被銷毀
std::weak_ptr<std::vector<std::string>> wptr;
std:size_t curr; // 在數(shù)組中的當前位置
}
默認構(gòu)造函數(shù)生成一個空的 StrBlobPtr瘾晃,其構(gòu)造函數(shù)初始化列表將 curr 顯式初始化為 0,并將 wptr 隱式初始化為一個空 weak_ptr幻妓,第二個構(gòu)造函數(shù)接受一個 StrBlob 引用和一個可選的索引值蹦误,此構(gòu)造函數(shù)初始化 wptr,令其指向給定 StrBlob 對象的 shared_ptr 中的 vector肉津,并將 curr 初始化為 sz 的值强胰。我們使用了默認參數(shù),表示默認情況下將 curr 初始化為第一個元素的下標妹沙。我們將會看到偶洋,StrBlob 的 end 成員將會用到參數(shù) sz。
值得注意的是初烘,我們不能將 StrBlobPtr 綁定到一個 const StrBlob 對象涡真。這個限制是由于構(gòu)造函數(shù)接受一個非 const StrBlob 對象的引用而導(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
}
由于一個 weak_ptr 不參與其對應(yīng)的 shared_ptr 的引用計數(shù)吗铐,StrBlobPtr 指向的 vector 可能已經(jīng)被釋放了东亦。如果 vector 已銷毀,lock 將返回一個空指針。在本例中典阵,任何 vector 的引用都會失敗奋渔,于是拋出一個異常。否則壮啊,check 會檢查給定索引嫉鲸,如果索引值合法,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) 是對象所指向的 vector
}
如果 check 成功藤树,p 就是一個 shared_ptr,指向 StrBlobPtr 所指向的 vector拓萌。表達式 (*p)[curr] 解引用 shared_ptr 來獲得 vector岁钓,然后使用下標運算符提取并返回 curr 位置上的元素。
incr 成員也調(diào)用 check:
// 前綴遞增:返回遞增后的對象的引用
StrBlobPtr& StrBlobPtr::incr() {
// 如果 curr 已經(jīng)指向容器的尾后位置微王,就不能遞增它
check(curr, "increment past end of StrBlobPtr");
++curr; // 推進當前位置
return *this;
}
當然屡限,為了訪問 data 成員,我們的指針類必須聲明為 StrBlob 的 friend 我們還要為 StrBlob 類定義 begin 和 end 操作骂远,返回一個指向它自身的 StrBlobPtr:
// 對于 StrBlob 中的友元聲明來說囚霸,此前置聲明是必要的
class StrBlobPtr;
class StrBlob {
friend class StrBlobPtr;
// 其他成員
// 返回指向首元素和尾后元素的 StrBlobPtr
StrBlobPtr begin() {return StrBlobPtr(*this); }
StrBlobPtr end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
};