weak_ptr类
weak_ptr是一种不控制所指向对象生存期的智能指针,它指向一个由一个shared_ptr管理的对象。将一个weak_ptr绑定到shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象还是会被释放
weak_ptr<T> w //空weak_ptr可以指向类型为T的对象
weak_ptr<T> w(sp) //与shared_ptr sp指向相同对象的weak_ptr。T必须能转化为sp指向的类型
w = p //p可以是一个shared_ptr或一个weak_ptr。赋值后w与p共享对象
w.reset() //将w置为空
w.use_count() //与w共享对象的shared_ptr的数量
w.expired() //若w.use_count()为0,返回true,否则返回false
w.lock() //如果expired为true,返回一个空shared_ptr,否则返回一个指向w的对象的shared_ptr
当我们创建一个weak_ptr时,要用一个shared_ptr来初始化它:
auto p = make_shared<int>(42);
weak_ptr<int> wp(p); //wp弱共享p,p的引用计数未改变
由于对象可能不存在,我们不能使用weak_ptr直接访问对象,而必须调用lock。此函数检查weak_ptr指向的对象是否存在。如果存在,lock返回一个指向共享对象的shared_ptr。
if (shared_ptr<int> np = wp.lock()){ //如果np不为空,则条件成立
//在if中,np与p共享对象
}
核查指针类
我们将为StrBlob类定义一个伴随指针类。我们的指针类将命名为StrBlobPtr,会保存一个weak_ptr,指向StrBlob的data成员,这是初始化时提供给它的。通过使用weak_ptr,不会影响一个给定的StrBlob所指向的vector的生存期。但是,可以阻止用户访问一个不再存在的vector的企图。
StrBlobPtr会有两个数据成员:wptr或者为空,或者指向一个StrBlob中的vector;curr,保存当前对象所表示的元素的下标。类似它的伴随类StrBlob,我们的指针也有一个check成员来检查解引用StrBlobPtr是否安全:
//对于访问一个不存在元素的尝试,StrBlobPtr抛出一个异常
class StrBlobPtr {
friend class StrBlob;
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) { }
string& deref() const;
StrBlobPtr& incr(); //前缀递增
private:
//若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t, const string&) const;
//保存一个weak_ptr,意味着底层vector可能被销毁
weak_ptr<vector<string>> wptr;
size_t curr; //在数组中的当前位置
};
默认构造函数生成一个空的StrBlobPtr。其构造函数初始化列表将curr显式初始化为0,并将wptr隐式初始化为一个空weak_ptr。
第二个构造函数接受一个StrBlob引用和一个可选的索引值。此构造函数初始化wptr,令其指向给定StrBlob对象的shared_ptr中的vector,并将curr初始化为sz的值。我们使用了默认参数,表示默认情况下将curr初始化为第一个元素的下标。
shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string& msg) const {
auto ret = wptr.lock(); //vector还存在吗?若存在返回一个指向w的对象的shared_ptr
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret; //否则,返回指向vector的shared_ptr
}
如果vector已经销毁,lock将返回一个空指针。在本例中,任何vector的引用都会失败,于是抛出一个异常。否则,check会检查给定索引,如果索引值合法,check返回从lock获得的shared_ptr。
指针操作
定义名为deref和incr的函数,分别用来解引用和递增StrBlobPtr
deref成员调用check,检查使用vector是否安全以及curr是否在合法范围内:
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位置上的元素。
//前缀递增:返回递增后的对象的引用
StrBlobPtr& StrBlobPtr::incr() {
//如果curr已经指向容器的尾后位置,就不能递增它
check(curr, "increment past end of StrBlobPtr");
++curr; //推进当前位置
return *this;
}
为了访问data成员,我们的指针类必须声明为StrBlob的friend。我们还要为StrBlob类定义begin和end操作,返回一个指向它自身的StrBlobPtr:
class StrBlobPtr;
class StrBlob{
StrBlobPtr begin(){return StrBlobPtr(*this);}
StrBlobPtr end(){
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
}
定义你自己版本的StrBlobPtr,更新StrBlob类,加入适当的friend声明以及begin和end成员
#include
#include
#include
#include
#include
using namespace std;
//提前声明,StrBlob中的friend类声明所需
class StrBlob {
friend class StrBlobPtr;
public:
typedef vector<string>::size_type size_type;
StrBlob(); //默认构造函数分配一个空的vector
//接受一个initializer_list的构造函数将其参数传递给对应的vector构造函数。此构造函数通过拷贝列表中的值来初始化vector中的元素
StrBlob(initializer_list<string> il);
size_type size() const { return data->size(); }
bool empty() const { return data->empty(); }
//添加和删除元素
void push_back(const string& t) { data->push_back(t); }
void pop_back();
//元素访问
string& front();
const string& front() const;
string& back();
const string& back() const;
//提供给StrBlobPtr的接口
StrBlobPtr begin();
StrBlobPtr end();
private:
shared_ptr<vector<string>> data;
//如果data[i]不合法则抛出一个异常
void check(size_type i, const string& msg) const;
};
inline StrBlob::StrBlob():data(make_shared<vector<string>>()){ }
inline StrBlob::StrBlob(initializer_list<string> il) : data(make_shared<vector<string>>(il)) {};
inline void StrBlob::check(size_type i, const string& msg) const {
if (i >= data->size())
throw out_of_range(msg);
}
inline string& StrBlob::front() {
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
inline const string& StrBlob::front() const{
//如果vector为空,check会抛出一个异常
check(0, "front on empty StrBlob");
return data->front();
}
inline string& StrBlob::back() {
check(0, "back on empty StrBlob");
return data->back();
}
inline const string& StrBlob::back() const{
check(0, "back on empty StrBlob");
return data->back();
}
inline void StrBlob::pop_back() {
check(0, "pop_back on empty StrBlob");
data->pop_back();
}
class StrBlobPtr {
friend bool eq(const StrBlobPtr&, const StrBlobPtr&); //StrBlobPtr的比较操作
public:
StrBlobPtr() : curr(0) { }
StrBlobPtr(StrBlob& a, size_t sz = 0) : wptr(a.data), curr(sz) { }
string& deref() const; //指针操作
StrBlobPtr& incr(); //前缀递增
StrBlobPtr& decr(); //前缀递减
private:
//若检查成功,check返回一个指向vector的shared_ptr
shared_ptr<vector<string>> check(size_t, const string&) const;
//保存一个weak_ptr,意味着底层vector可能被销毁
weak_ptr<vector<string>> wptr;
size_t curr; //在数组中的当前位置
};
inline shared_ptr<vector<string>> StrBlobPtr::check(size_t i, const string& msg) const {
auto ret = wptr.lock(); //vector还存在吗?若存在返回一个指向w的对象的shared_ptr
if (!ret)
throw runtime_error("unbound StrBlobPtr");
if (i >= ret->size())
throw out_of_range(msg);
return ret; //否则,返回指向vector的shared_ptr
}
inline string& StrBlobPtr::deref() const {
auto p = check(curr, "dereference past end");
return (*p)[curr]; //(*p)是对象所指的vector
}
//前缀递增:返回递增后的对象的引用
inline StrBlobPtr& StrBlobPtr::incr() {
//如果curr已经指向容器的尾后位置,就不能递增它
check(curr, "increment past end of StrBlobPtr");
++curr; //推进当前位置
return *this;
}
//前缀递减:返回递减后对象的引用
inline StrBlobPtr& StrBlobPtr::decr() {
//如果curr已经为0,递减它就会产生一个非法下标
--curr; //递减当前位置
check(-1, "decrement past begin of StrBlobPtr");
return *this;
}
inline StrBlobPtr StrBlob::begin() {
return StrBlobPtr(*this);
}
inline StrBlobPtr StrBlob::end() {
auto ret = StrBlobPtr(*this, data->size());
return ret;
}
inline bool eq(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
auto l = lhs.wptr.lock(), r = rhs.wptr.lock();
//若底层的vector是同一个
if (l == r)
//则两个指针都是空或者指向相同的元素
return (!r || lhs.curr == rhs.curr);
else
return false; //若指向不同vector,则不相等
}
inline bool neq(const StrBlobPtr& lhs, const StrBlobPtr& rhs) {
return !eq(lhs, rhs);
}
int main() {
StrBlob b1;
{
StrBlob b2 = { "a", "an", "the" };
b1 = b2;
b2.push_back("about");
cout << b2.size() << endl;
}
cout << b1.size() << endl;
cout << b1.front() << " " << b1.back() << endl;
const StrBlob b3 = b1;
cout << b3.front() << " " << b3.back() << endl;
cout << endl;
for (auto it = b1.begin(); neq(it, b1.end()); it.incr())
cout << it.deref() << endl;
return 0;
}