std::bind绑定std::shared_ptr出现的问题

using msgCallback = std::function<void()>;

class A {
public:
    ~A() { 
        cout << "A::~A()" << endl; 
    }
    void output() {
        cout << "A::output()" << endl;
    }
};

class B {
public:
    ~B() {
        cout << "B::~B()" << endl;
    }
    void setCallback(const msgCallback& cb) {
        if (m_cb == nullptr)
            m_cb = cb;
    }
    void resetCallback() {
        m_cb = nullptr;
    }
private:
    msgCallback m_cb;
};

A未能按预期析构:

int main()
{
    shared_ptr<B> spb = make_shared<B>();
    {
        shared_ptr<A> spa = make_shared<A>();   //use_count:1

        list<shared_ptr<A>> lst;
        lst.push_back(spa);     //use_count:2
        cout << "count1: " << spa.use_count() << endl;
        {
            std::function<void()> f = std::bind(&A::output, spa);   //use_count:3
            spb->setCallback(f);    //use_count:4
            cout << "count2: " << spa.use_count() << endl;
        }   //use_count:3
        cout << "count3: " << spa.use_count() << endl;

        if (!lst.empty())
            lst.erase(lst.begin());     //use_count:2
        cout << "count4: " << spa.use_count() << endl;
    }   //use_count:1

    //期望spa在lst.erase后就析构
    //结果spb->m_cb还具有spa的一份引用,导致A不能按预期析构

    system("pause");
    return 0;
}

A正常析构:

int main()
{
    {
        shared_ptr<A> spa = make_shared<A>();   //use_count:1
        shared_ptr<B> spb = make_shared<B>();

        list<shared_ptr<A>> lst;
        lst.push_back(spa);     //use_count:2
        cout << "count1: " << spa.use_count() << endl;
        {
            std::function<void()> f = std::bind(&A::output, spa);   //use_count:3
            spb->setCallback(f);    //use_count:4
            cout << "count2: " << spa.use_count() << endl;
        }   //use_count:3
        cout << "count3: " << spa.use_count() << endl;

        if (!lst.empty())
            lst.erase(lst.begin());     //use_count:2
        cout << "count4: " << spa.use_count() << endl;

        spb->resetCallback();   //use_count:1
        cout << "count5: " << spa.use_count() << endl;
    }   //use_count:0

    system("pause");
    return 0;
}

当std::bind绑定shared_ptr时,shared_ptr引用计数加一,
这时候认为function f保持一份引用,只有f析构了引用计数才减一
当我将function传给B::m_cb时,B::m_cb保持一份引用

所以要想spa在list::erase的时候析构,必须提前将spb_m_cb重置为nullptr
但是这样就对A的生命周期管理造成了负担。
所以当我们想要准确的控制对象的生命周期的时候,用裸指针可能更合适。

还有一种情况用裸指针可能好点,例如TcpConnection类和Channel类:
如果用shared_ptr互相包含,可能会出现智能指针循环引用的情况。
而用裸指针,可以很方便的控制Channel在TcpConnection析构前析构。

如果真的要用shared_ptr管理对象生命周期,那就让shared_ptr的引用计数保持为1,也就是让某个对象具有这个shared_ptr实例,
其它地方全部传shared_ptr的引用。当这个对象析构了,shared_ptr就随之析构。
还有一点:为了避免引用计数增加,我们用std::bind绑定指针的时候,可以直接传this指针,而不是shared_ptr。

为了避免shared_ptr的循环引用,可以用weak_ptr

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值