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

C/C++ 专栏收录该内容
25 篇文章 0 订阅


结合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




  • 0
    点赞
  • 0
    评论
  • 5
    收藏
  • 一键三连
    一键三连
  • 扫一扫,分享海报

©️2021 CSDN 皮肤主题: 编程工作室 设计师:CSDN官方博客 返回首页
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值