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

本文探讨了在C++中使用std::shared_ptr时,如何避免循环引用和正确管理对象生命周期的问题。通过示例说明了在特定场景下,如TcpConnection和Channel类的设计中,使用裸指针可能比智能指针更适合,以精确控制对象的销毁时机。

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

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

--- stderr: card_drawing2 In file included from /usr/include/c++/11/pstl/glue_algorithm_defs.h:13, from /usr/include/c++/11/algorithm:74, from /opt/ros/humble/include/rclcpp/rclcpp/executor.hpp:18, from /opt/ros/humble/include/rclcpp/rclcpp/executors/multi_threaded_executor.hpp:25, from /opt/ros/humble/include/rclcpp/rclcpp/executors.hpp:21, from /opt/ros/humble/include/rclcpp/rclcpp/rclcpp.hpp:155, from /home/f972/assi_ws/src/card_drawing2/src/card_server.cpp:1: /usr/include/c++/11/functional: In instantiation of ‘struct std::_Bind_check_arity<void (CardServer::*)(std::shared_ptr<card_interface::srv::String_Response_<std::allocator<void> > >), CardServer*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>’: /usr/include/c++/11/functional:768:12: required from ‘struct std::_Bind_helper<false, void (CardServer::*)(std::shared_ptr<card_interface::srv::String_Response_<std::allocator<void> > >), CardServer*, const std::_Placeholder<1>&, const std::_Placeholder<2>&>’ /usr/include/c++/11/functional:789:5: required by substitution of ‘template<class _Func, class ... _BoundArgs> typename std::_Bind_helper<std::__is_socketlike<_Func>::value, _Func, _BoundArgs ...>::type std::bind(_Func&&, _BoundArgs&& ...) [with _Func = void (CardServer::*)(std::shared_ptr<card_interface::srv::String_Response_<std::allocator<void> > >); _BoundArgs = {CardServer*, const std::_Placeholder<1>&, const std::_Placeholder<2>&}]’ /home/f972/assi_ws/src/card_drawing2/src/card_server.cpp:13:38: required from here /usr/include/c++/11/functional:756:21: error: static assertion failed: Wrong number of arguments for pointer-to-member 755 | static_assert(_Varargs::value | ~~~~~ 756 | ? sizeof...(_BoundArgs) >= _Arity::value + 1 | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
03-31
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值