智能指针(二) shared_ptr 注意点

25 篇文章 1 订阅

智能指针(二) shared_ptr 注意点

1 不存在 int * 到 shared_ptr 的隐式类型转换

void proc(shared_ptr<int> ptr)
{
    cout << "ptr.use_count()=" << ptr.use_count() << endl;
    cout << "调用成功" << endl;
    return;
}
  int *p = new int(100);

    // todo  1 不存在 int * 到 shared_ptr<int> 的隐式类型转换
    //  proc(p);

2切勿传递临时引用作为函数参数


void proc(shared_ptr<int> ptr)
{
    cout << "ptr.use_count()=" << ptr.use_count() << endl;
    cout << "调用成功" << endl;
    return;
}
  int *p = new int(100);
proc(std::shared_ptr<int>(p)); // 参数是临时的 shared_ptr,用裸指针显示构造
    //  传递到 proc 函数中的引用计数为 1 ,离开函数体后,引用计数为 0 , 临时构造的shared_ptr指针所指向的内存(也就是p 指向的内存) 被 回收
   *p =25; //此时操作p将是违法的(已置空)
  • 把一个普通裸指针绑定到一个shared_ptr 上之后, 内存管理的责任就交给这个shared_ptr了,这个时候就不应该用裸指针来访问shared_ptr所指向的内存了

修正的做法是

 shared_ptr<int> pi(p);
    proc(pi);
    *pi = 45; // 操作安全
    // 传递到 proc 函数中的引用计数为 2 ,离开函数体后,引用计数为 1 ,pi 指向的对象 不会被回收。

3严禁 用裸指针初始化多个 shared_ptr

  • 多个shared_ptr 并不会增加引用计数,内存管理将不会贯通(初始化 shared_ptr,但二者并不指向同一个控制块,引用计数不会累计),
 int *p = new int(100);
    void func()
    {
        shared_ptr<int> p1(p);
        cout << "p1.use_count()" << p1.use_count() << endl;
        shared_ptr<int> p2(p);
        cout << "p1.use_count()" << p1.use_count() << endl;
        // 当 p1 p2 的生命结束时, 指向的对象将被 释放两次(因为p1和p2引用计数均为1,而且p2 是释放同一块已经释放内存的空间)

        // 应用下面的方式代替
        /*
        shared_ptr<int> p1(new int); // 用匿名裸指针 初始化
        shared_ptr<int> p2(p1); //用p1 初始化 其他 shared_ptr


        */
    }

4谨慎 使用 get 返回的裸指针

  • 注意 :get() 返回的裸指针不能delete,否则会异常
 void func()
    {
        cout << "detail2::func()" << endl;
        shared_ptr<int> pi(new int);
        int *p = pi.get();
        delete p;// error
    }
  • 不能将智能指针 绑定到get 返回的指针上
void func2()
    {

        // ?. 类似于把裸指针赋值给了两个shared_ptr
        shared_ptr<int> pi(new int);
        cout << "pi.use_count()" << pi.use_count() << endl;
        int *p = pi.get();
        shared_ptr<int> pi2(p);
        cout << "pi.use_count()" << pi.use_count() << endl;
        cout << "pi2.use_count()" << pi2.use_count() << endl;
    }

5 不要返回类对象指针

  • 使用类对象this初始化另一个对象, 引用计数将不会增加
 class TC
    {
    public:
        shared_ptr<TC> get_self_this()
        {
            return shared_ptr<TC>(this);
        }
    };
     void func()
    {
        cout << "detail::func()" << endl;
        shared_ptr<TC> p1(new TC);
        cout << "p1.use_count()" << p1.use_count() << endl;
        shared_ptr<TC> p2 = p1->get_self_this();

        cout << "p1.use_count()" << p1.use_count() << endl;
    }
  • 使用 shared_from_this 代替
class TC2 : public enable_shared_from_this<TC2>
    {
    public:
        shared_ptr<TC2> get_self_enable_shared()
        {
            // /这个就是enable_shared_from_this类中的方法,要通过此方法返回智能指针
            return shared_from_this();
        }
    };
    
      void func2()
    {
        cout << "detail2::func2()" << endl;
        shared_ptr<TC2> p1(new TC2);
        cout << "p1.use_count()" << p1.use_count() << endl;
        shared_ptr<TC2> p2 = p1->get_self_enable_shared();

        cout << "p1.use_count()" << p1.use_count() << endl;
    }

6 循环引用问题

 class CB;
    class CA
    {
    public:
        CA() { cout << "CA() called! " << endl; }
        ~CA() { cout << "~CA() called! " << endl; }
        void set_ptr(shared_ptr<CB> &ptr) { m_ptr_b = ptr; }
        void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
        void show() { cout << "this is class CA!" << endl; }

    private:
        shared_ptr<CB> m_ptr_b;
    };

    class CB
    {
    public:
        CB() { cout << "CB() called! " << endl; }
        ~CB() { cout << "~CB() called! " << endl; }
        void set_ptr(shared_ptr<CA> &ptr) { m_ptr_a = ptr; }
        void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
        void show() { cout << "this is class CB!" << endl; }

    private:
        shared_ptr<CA> m_ptr_a;
    };

    void test_refer_to_each_other()
    {
        shared_ptr<CA> ptr_a(new CA());
        shared_ptr<CB> ptr_b(new CB());

        cout << "a use count : " << ptr_a.use_count() << endl;
        cout << "b use count : " << ptr_b.use_count() << endl;

        ptr_a->set_ptr(ptr_b);
        ptr_b->set_ptr(ptr_a);

        cout << "a use count : " << ptr_a.use_count() << endl;
        cout << "b use count : " << ptr_b.use_count() << endl;
    }
    int main()
    {
    test_refer_to_each_other();
    }
CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 2
b use count : 2
  • 通过结果可以看到,最后CA和CB的对象并没有被析构,其中的引用效果如下图所示,起初定义完ptr_a和ptr_b时,只有 ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用,
  • 然后调用函数set_ptr后又增加了m_ptr_a 指向 new CA() 和 m_ptr_b指向 new CB 两条引用,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,
  • 也就是【ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用 】会被断开,但是依然m_ptr_a 指向 new CA() 和 m_ptr_b指向 new CB 两条引用存在,
  • 每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构(不能调用析构函数),造成内存泄漏。

wk_shared_ptr

解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:

  class CB;
    class CA
    {
    public:
        CA() { cout << "CA() called! " << endl; }
        ~CA() { cout << "~CA() called! " << endl; }
        void set_ptr(shared_ptr<CB> &ptr) { m_ptr_b = ptr; }
        void b_use_count() { cout << "b use count : " << m_ptr_b.use_count() << endl; }
        void show() { cout << "this is class CA!" << endl; }

    private:
        shared_ptr<CB> m_ptr_b;
    };
    /*
    todo 解决这种状况的办法就是将两个类中的一个成员变量改为weak_ptr对象,因为weak_ptr不会增加引用计数,使得引用形不成环,
    todo 最后就可以正常的释放内部的对象,不会造成内存泄漏,比如将CB中的成员变量改为weak_ptr对象,代码如下:
    */
    class CB
    {
    public:
        CB() { cout << "CB() called! " << endl; }
        ~CB() { cout << "~CB() called! " << endl; }
        void set_ptr(shared_ptr<CA> &ptr) { m_ptr_a = ptr; }
        void a_use_count() { cout << "a use count : " << m_ptr_a.use_count() << endl; }
        void show() { cout << "this is class CB!" << endl; }

    private:
        weak_ptr<CA> m_ptr_a;
    };

    void test_refer_to_each_other()
    {
        shared_ptr<CA> ptr_a(new CA());
        shared_ptr<CB> ptr_b(new CB());

        cout << "a use count : " << ptr_a.use_count() << endl;
        cout << "b use count : " << ptr_b.use_count() << endl;

        ptr_a->set_ptr(ptr_b);
        ptr_b->set_ptr(ptr_a);

        cout << "a use count : " << ptr_a.use_count() << endl;
        cout << "b use count : " << ptr_b.use_count() << endl;
    }

测试结果如下:

CA() called!
CB() called!
a use count : 1
b use count : 1
a use count : 1
b use count : 2
~CA() called!
~CB() called!
  • 通过这次结果可以看到,CA和CB的对象都被正常的析构了,*
  • 但是不同的是m_ptr_a指向new CA()这条引用是通过weak_ptr建立的,并不会增加引用计数,也就是说CA的对象只有一个引用计数,*
  • t而CB的对象只有2个引用计数,当test_refer_to_each_other这个函数返回时,对象ptr_a和ptr_b被销毁,
  • 也就是 ptr_a 指向new_CA() 和ptr_b 指向 new CB()两条引用 会被断开,此时CA对象的引用计数会减为0,对象被销毁,*
  • *其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。

wk_weak_ptr

7移动语义

  • 使用 std::move 可以将智能指针拥有权 转交给另一个智能指针,而不引起引用计数的增加
 void func()
    {
        cout << "detail6::func" << endl;
        shared_ptr<int> si = make_shared<int>(100);
        cout << "si.use_count()" << si.use_count() << endl;
        auto si2 = si;
        cout << "si.use_count()" << si.use_count() << endl;
        cout << "si2.use_count()" << si2.use_count() << endl;
    }
    void func2()
    {
        cout << "detail6::func2" << endl;
        shared_ptr<int> si = make_shared<int>(100);
        cout << "si.use_count()" << si.use_count() << endl;
        auto si2 = std::move(si);

        cout << "si.use_count()" << si.use_count() << endl;
        cout << "si2.use_count()" << si2.use_count() << endl;
    }

<< “detail6::func2” << endl;
shared_ptr si = make_shared(100);
cout << “si.use_count()” << si.use_count() << endl;
auto si2 = std::move(si);

    cout << "si.use_count()" << si.use_count() << endl;
    cout << "si2.use_count()" << si2.use_count() << endl;
}

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

丁金金

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值