您的位置:首页 > 其它

shared_ptr和weak_ptr智能指针结合使用的一个实例

2016-10-29 20:21 701 查看
结合shared_ptrweak_ptr一个实例
         感觉这个例子很好, 结合了很多知识技术。这个实例功能是简单模拟实现std::vector<std::string> 部分功能。 (只是非常简单一些操作),当然也可以继续扩展,甚至扩展为模板,主要是用于学习。使用两个类实现,分别是StrBlobStrBlobPtr
StrBlob主要使用shared_ptr类对象来管理资源,StrBlobPtr主要使用weak_ptr类对象来管理资源。底层操作数据对象

std::vector<std::string>


学习点: (真心感觉这个例子很棒)

1、StrBlobStrBlobPtr的关系,基本上类似容器和迭代器的关系,很好将他们区分开来。

2、对内存资源的管理,非常巧妙。

3、对异常考虑

下面是本例的完整代码, 然后再简单分析一下自己理解。

verStr.h

#ifndef VECSTR_H_INCLUDED
#define VECSTR_H_INCLUDED
#include<memory>
#include<vector>
#include<string>

class StrBlobPtr;

class StrBlob
{
public:
using size_type = std::vector<std::string>::size_type;

// in order to access the data member in StrBlobPtr class
friend class StrBlobPtr;
//constructor
StrBlob()
:data(std::make_shared<std::vector<std::string> >()) {}
StrBlob(const std::initializer_list<std::string> lisStr)
: data(std::make_shared<std::vector<std::string> >(lisStr)) {}

//the operator of size
size_type size() const{ return data->size();}
bool empty() const { return data->empty();}

//add and remove elements
void push_back(const std::string& str) {data->push_back(str);}
void pop_back()
{
check(0,"pop_back on empty StrBlob");
data->pop_back();
}

//element access
std::string& front() const
{
check(0,"front on empty StrBlob");
return data->front();
}
std::string& back() const
{
check(0,"back on empty StrBlob");
return data->back();
}

//return StrBlobPtr to the first and one past the last elements
StrBlobPtr begin();
StrBlobPtr end();
private:
std::shared_ptr<std::vector<std::string> > data;

// throws msg if data[_size] isn't valid
void check(size_type _size,const std::string& msg) const
{
if(_size>=data->size())
throw std::runtime_error(msg);
}
};

class StrBlobPtr
{
public:
StrBlobPtr() :cur(0){}
StrBlobPtr(StrBlob& str,std::size_t sz = 0)
:wptr(str.data),cur(sz)
{

}
// operator of elements
bool operator!=(const StrBlobPtr& p) { return p.cur!= cur; }
std::string& deref() const;
StrBlobPtr& incr(); // prefix version

private:
std::shared_ptr<std::vector<std::string> >
check(std::size_t ,const std::string& msg) const;

//store a weak_ptr, which means the underlying vector might be destroyed
std::weak_ptr<std::vector<std::string> > wptr;
std::size_t cur;  // current position within the array
};

std::shared_ptr<std::vector<std::string> >
StrBlobPtr::check(std::size_t _size,const std::string& msg) const
{
auto ret = wptr.lock(); //return the shared_ptr pointer to the object to which wptr points from lock to initialize ret
if(!ret)
throw std::runtime_error("unbound StrBlobPtr");
if(_size>=ret->size())
throw std::out_of_range(msg);
return ret;
}
std::string& StrBlobPtr::deref() const
{
auto p = check(cur,"dereference past end");
return (*p)[cur];
}

StrBlobPtr& StrBlobPtr::incr()
{
check(cur,"increment past end of StrBlobPtr");
++cur;
return *this;
}

StrBlobPtr StrBlob::begin()
{
return StrBlobPtr(*this);
}
StrBlobPtr StrBlob::end()
{
auto ret = StrBlobPtr(*this,data->size());
return ret;
}

#endif // VECSTR_H_INCLUDED


以下是一个测试用例,从一个文件中读取字符串,将其保存再StrBlob中,再将其打印出来。

#include <iostream>
#include<fstream>
#include<sstream>
#include<algorithm>
#include"vecStr.h"
const std::string fileName = "input.txt";
int main()
{
StrBlob str;
std::ifstream fs(fileName);
std::string buffer;
while(getline(fs,buffer))
str.push_back(buffer);

for(StrBlobPtr it = str.begin();it!=str.end(); it.incr())
{
std::cout<<it.deref()<<std::endl;
}
return 0;
}


简单分析:(以下资源是指std::vector<std::string>

shared_ptr、weak_ptr具体类型如下

std::shared_ptr<std::vector<std::string> > data;
std::weak_ptr<std::vector<std::string> > wptr;

我们知道,我们用StrBlob去显示初始化StrBlobPtr对象, 主要是使用data去初始化wptr

StrBlobPtr(StrBlob& str,std::size_t sz = 0)
:wptr(str.data),cur(sz)
{

}


当构造完StrBlobPtr对象,shared_ptr所管理的资源用户是不会增加的(即资源引用计数不会自增一),weak_ptr只是指向一块被shared_ptr管理的资源,所拥有的这份资源的管理权在shared_ptr, 这也导致我们在使用weak_ptr所指向的资源的时候,需要检查一下它所指向的资源是否存在(因为shared_ptr所管理资源可能被删除)。
 在StrBlobPtrcheck中函数实现。当weak_ptr所指向的资源不存在时,便抛出一个异常runtime_error

std::shared_ptr<std::vector<std::string> >
StrBlobPtr::check(std::size_t _size,const std::string& msg) const
{
auto ret = wptr.lock(); //return the shared_ptr pointer to the object to which wptr points from lock to initialize ret
if(!ret)
throw std::runtime_error("unbound StrBlobPtr");
if(_size>=ret->size())
throw std::out_of_range(msg);
return ret;
}


这里还有一个地方需要注意,weak_ptr是没有办法直接操作它所指向的资源的(一个原因是它所指向的资源有可能被删除),需要借助shared_ptr对象,通过调用wptr.lock(),当wptr指向非空时,返回一个shared_ptr对象,指向的wptr所指向的资源, 并用这个对象去初始化ret(创建了一个shared_pre对象名ret,这时候weak_ptr所返回的资源管理用户会增加多一个,即ret这个用户,资源引用计数增加一个)。如果wptr指向空,那么这时候lock会返回一个nullptr空指针,程序会抛出一个异常。

现在问题又来了,ret这个用户作为check函数的返回值,意味着ret这个用户并没有在check函数结束后释放对它所指向的资源的管理权(资源引用不会减少)那让我们来跟踪一个ret这个用户去哪里了。 StrBlobPtr类有derefincr成员调用这个函数,如下。

std::string& StrBlobPtr::deref() const
{
auto p = check(cur,"dereference past end");
return (*p)[cur];
}

StrBlobPtr& StrBlobPtr::incr()
{
check(cur,"increment past end of StrBlobPtr");
++cur;
return *this;
}


显然当这两个函数分别执行完后,ret将作为函数中局部对象销毁了,这时候(资源引用计数递减1)。 非常巧妙,非常简单,这样便保证了资源不会泄漏。

在仔细看看,StrBlobPtr对象是如何解引用获取StrBlob对象的值,这里并不是在StrBlobPtr类中重载operator *实现解引用。而是使用了refef成员函数代替(为了方便),

容易看到是通过shared_ptr对象操作下标cur,取得了该资源(vector<string>)所对应的值( 相当于(*data)[cur]).

再来看看如何对StrBlobPtr对象实现递增,这里同样也不是重载operator ++()或者是operator ++(int)  分别对应前置递增跟后置递增, 这里使用incr()函数,结合数据成员

std::weak_ptr<std::vector<std::string> > wptr;
std::size_t cur;  // current position within the array


模拟实现前置递增,很容易看出来。

想想上述对象是否能够满足创建const StrBlob const StrBlobPtr 对象??

显然是不符合我们使用习惯,需要再重载如下函数:

StrBlob中(以下只提供声明声明,实现类似)

StrBlobPtr begin() const ;
StrBlobPtr end();  const;


StrBlobPtr中 

const std::string& deref() const;


以上分析是个人理解,有不妥欢迎指出来。

参考:

C++Primer 5th (12.1.6 weak_ptr)

原文:http://blog.csdn.net/qq_33850438/article/details/52955326
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: