从shared_from_this引发的一个错误看weak_ptr的应用

从shared_from_this引发的一个错误看weak_ptr的应用


关于循环引用的概念一直了解,但理解并不深入。一直在使用shared_ptr,直到在项目中编写一个资源管理类时,指针在层级间流转造成运行时抛出std::bad_weak_ptr

在BS的《C++程序设计语言》中指出,应该尽量优先考虑使用std::unique_ptr而非std::shared_ptr。关于这一点,限于经验,在实际使用中总是很迷惑资源是否不会被共享。这个问题对我来说只能勤加思考,多多实践了。

关于循环引用的概念不多介绍,自行搜索即可。

问题

class CachePool;

class CConn final
{
public:
    CConn(const std::shared_ptr<CPool>& pCPool) 
    	: m_pCPool{pCPool}
    {  }
    ~CConn() { }
private:
    const std::shared_ptr<CPool> m_pCPool;
};




class CPool final
    : std::enable_shared_from_this<CPool>
{
    const int MAX_CACHE_CONN_CNT = 2;
public:
    CPool();
    ~CPool();

    int Init()
    {
    	for (int i = 0; i < m_cur_conn_cnt; ++i){
        	auto pConn = std::make_shared<CConn>(shared_from_this());
        	m_free_list.push_back(pConn);
    	}
    	return 0;
	}

private:
    int m_cur_conn_cnt = MAX_CACHE_CONN_CNT;
    std::list<std::shared_ptr<CConn>> m_free_list;
};

该代码在运行时抛出std::bad_weak_ptr异常。

在这里插入图片描述

可以看到该代码,在管理类CPool中拥有一个队列m_free_list中存储着资源类CConn的智能指针列表,而在资源类中拥有一个指向管理类的std::shared_ptr,用以访问管理类的接口,以提供对各个资源类都适用的信息。

以此形成循环引用。

解决方案

舍弃资源类中指向管理类的指针

可以将管理类中被资源类所需要的信息,在资源类的构造过程中传递给资源类,在资源类中以const &的形式进行存储与访问。

class CachePool;

class CConn final
{
public:
    CacheConn(const int& cur_conn_cnt;) 
    	: pool_cur_conn_cnt{cur_conn_cnt}
    {  }
    ~CacheConn() { }
private:
    const int& pool_cur_conn_cnt;
};




class CPool final
    : std::enable_shared_from_this<CachePool>
{
    const int MAX_CACHE_CONN_CNT = 2;
public:
    CachePool();
    ~CachePool();

    int Init()
    {
    	for (int i = 0; i < m_cur_conn_cnt; ++i){
        	auto pConn = std::make_shared<CConn>(m_cur_conn_cnt);
        	m_free_list.push_back(pConn);
    	}
    	return 0;
	}

private:
    int m_cur_conn_cnt = MAX_C_CONN_CNT;
    std::list<std::shared_ptr<CConn>> m_free_list;
};

这是一种限制性很大的解法。

其一,如果管理类中拥有的共享资源过多,导致资源类的参数列表过于繁冗。违反了接口简洁的设计原则。

其二,引用作为与指针类似概念的间接访问方式,拓展了资源的访问渠道,失去了std::shared_ptr提供的线程安全保障:

在这里插入图片描述

也就是说,在多线程情况下,裸资源的线程安全性转移在程序员手中来维护,需要在每个资源类的使用中进行同步,增大了很多工作量,以及复杂的同步性问题中的出错概率和效率损失。(很难保证加锁粒度对性能的影响是否比share_ptr更低)。

使用weak_ptr打破循环引用

class CachePool;

class CConn final
{
public:
    CConn(const std::weak_ptr<CPool>& pCPool) 
    	: m_pCPool{pCPool}
    {  }
    ~CConn() { }
private:
    const std::weak_ptr<CPool> m_pCPool;
};




class CPool final
    : std::enable_shared_from_this<CPool>
{
    const int MAX_CACHE_CONN_CNT = 2;
public:
    CachePool();
    ~CchePool();

    int Init()
    {
    	for (int i = 0; i < m_cur_conn_cnt; ++i){
        	auto pConn = std::make_shared<CConn>(weak_from_this());
        	m_free_list.push_back(pConn);
    	}
    	return 0;
	}

private:
    int m_cur_conn_cnt = MAX_CACHE_CONN_CNT;
    std::list<std::shared_ptr<CConn>> m_free_list;
};

编译通过。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值