C++Primer : 第十二章 :智能指针weak_ptr类

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 <iostream>
#include <initializer_list>
#include <vector>
#include <string>
#include <memory>
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;
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值