郁闷的Loki::SmartPtr

本文探讨了使用Loki库中的SmartPtr时遇到的引用计数问题,特别是当从原始指针构造SmartPtr时未正确递增引用计数导致资源管理错误。并提出了解决方案,包括手动调用AddRef以及修改源代码。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

最近做的项目,需要在多线程中管理资源,很自然就想到使用智能指针搭配引用计数的技术构造垃圾收集回收机制。首先考虑的是采用loki中的SmartPtr,并且已经在项目中运作,现在才发现Loki::SmartPtr并不是想象中的灵活好用。问题在于SmartPtr的构造函数(用原始指针构造出智能指针对象):
  SmartPtr(const StoredType& p) : SP(p)
        {
           
            SmartPtrGuard KPGuard(*this, &SmartPtr::OnKPReject);
            KP::OnInit(GetImpl(*this));
            KPGuard.Dismiss();
        }

当你传入一个原始指针给SmartPtr时,会调用该构造函数,这个构造函数并不会执行一次引用计数的递加运算,比如,当你写下这样的代码:
class CA;
void Fun()
{
 CA* pCA = new CA; // 引用计数为1
 Loki::SmartPtr<CA> spCA1(pA); //引用计数仍然为1
 Loki::SmartPtr<CA> spCA2(pA); //引用计数仍然为1
}

会出现什么问题?SmartPtr的缺省ownership策略为RefCounted,Fun返回之前,引用计数一直为1,但当Fun返回的时候,会执行spCA1和spCA2的析构函数,spCA1和spCA2都会递减一次引用计数。递加了1次,却要递减2次,这样使用,问题就出来了。
SmartPtr的这种构造函数不会进行一次AddRef,但是每个析构函数却会毫不犹豫的执行一次Release。
当然,这个问题很容易解决,在每次使用原始指针构造SmartPtr的时候,就调用一次AddRef.比如Fun函数这样写:
void Fun()
{
 CA* pCA = new CA; // 引用计数为1
 Loki::SmartPtr<CA> spCA1(pA); //引用计数仍然为1
 Loki::SmartPtr<CA> spCA2(pA); //引用计数仍然为1
 spCA2->AddRef(); //引用计数为2
}
而且,还要注意,spCA1构造完成后,不能调用AddRef,否则就多递加了一次。这种解决方法是很丑陋的,使用智能指针的目的之一就是让它自

己管理资源,当它内部和外部都参与了资源的管理,就显得混乱了。更郁闷的是,我目前发现根据自己定制相关策略也不能解决这类问题。Sma

rtPtr(const StoredType& p)构造函数不会像它的拷贝构造函数一样执行一次OP::Clone操作(内部调用了一次AddRef).最后,其实修改Loki的源代码很容易解决这个问题,在SmartPtr(const StoredType& p)函数的末尾加上一个OP::Clone();并且RefCounted策略构造时把refcount初始化为0;
而boost的intrusive_ptr就是这样做的(boost有好几个智能指针模板,由于我只关心侵入式的引用计数方式,所以只看了这一个的实现:():
    intrusive_ptr(T * p, bool add_ref = true): p_(p)
    {
        if(p_ != 0 && add_ref) intrusive_ptr_add_ref(p_);
    }
boost把用原始指针构造智能指针的问题交给用户来决定是否addref一次,并且缺省为执行一次addref。
以强大灵活著称的loki却在这里让我郁闷了。也许这就是设计观念的不同吧
 

评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值