c++ 智能指针 shared_ptr 与 weak_ptr 的使用

注意 编译的时候要指定 --std=c++11
例如

g++ myshareptr.cpp --std=c++11

1 智能指针 shared_ptr

1 关注事项

1 如何调用托管对象的成员 函数
2 reset函数 :释放托管对象的所有权(如果有),调用后,该智能指针不再管理任何对象。

代码示例

#include<iostream>
#include<string>
#include<memory>

using namespace std;

class Report
{
   private:
          string str;
   public:
          Report(const std::string s) :str(s)
          {
                   cout<<"Object created\n";
          }
          ~Report()
          {
                 cout<<"Object deleted @@@@@@@@@@@@\n";
	  }
          void comment() const
          {
		cout<<str<<"**************\n";
          }
          
};

int main()
{
  
   //shared_ptr<Report> ps(new Report("using share_ptr"));
   Report *pa = new Report("using share_ptr");
   shared_ptr<Report> ps(pa);
   shared_ptr<Report> ps1 = ps; // 再引用一次 (注意引用的主体是  shared_ptr)
   shared_ptr<Report> ps2 = ps;
   cout<<"count():  \n"<<ps.use_count()<<endl;
   cout<<"count():  \n"<<ps1.use_count()<<endl;
   cout<<"count():  \n"<<ps2.use_count()<<endl;
   ps->comment();  //注意: 通过 -> 调用管理指针的函数
   cout<<"++count():  \n"<<ps.use_count()<<endl;
   ps.reset();// 释放托管对象的所有权(如果有),调用后,该智能指针不再管理任何对象。
   cout<<"__count():  \n"<<ps.use_count()<<endl;
   cout<<"__count():  \n"<<ps1.use_count()<<endl;
   cout<<"__count():  \n"<<ps1.use_count()<<endl;
   
   ps1.reset();
   ps2.reset();
   return 0;
}

2 weak_ptr弱引用

一 shared_ptr 致命的问题就是 循环引用
1 在shared_ptr的使用过程中,当强引用计数为0时,就会释放所指向的堆内存。
2 但是如果和死锁一样,当两个shared_ptr互相引用,那么它们就永远无法被释放了。
例如:

#include <iostream>
#include <memory>

class CB;
class CA {
  public:
    CA() {
      std::cout << "CA()" << std::endl;
    }
    ~CA() {
      std::cout << "~CA()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CB>& ptr) {
      m_ptr_b = ptr;
    }
  private:
    std::shared_ptr<CB> m_ptr_b;
};

class CB {
  public:
    CB() {
      std::cout << "CB()" << std::endl;
    }
    ~CB() {
      std::cout << "~CB()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CA>& ptr) {
      m_ptr_a = ptr;
    }
  private:
    std::shared_ptr<CA> m_ptr_a;
};

int main()
{
  std::shared_ptr<CA> ptr_a(new CA());
  std::shared_ptr<CB> ptr_b(new CB());
  ptr_a->set_ptr(ptr_b);
  ptr_b->set_ptr(ptr_a);
  std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;

  return 0;
}

编译并运行结果,打印为:

CA()
CB()
2 2

没有调用析构函数:
在这里插入图片描述
起初定义完ptr_a和ptr_b时,只有①、③两条引用,即ptr_a指向CA对象,ptr_b指向CB对象。然后调用函数set_ptr后又增加了②、④两条引用,即CB对象中的m_ptr_a成员变量指向CA对象,CA对象中的m_ptr_b成员变量指向CB对象。

这个时候,指向CA对象的有两个,指向CB对象的也有两个。当main函数运行结束时,对象ptr_a和ptr_b被销毁,也就是①、③两条引用会被断开,但是②、④两条引用依然存在,每一个的引用计数都不为0,结果就导致其指向的内部对象无法析构,造成内存泄漏。


weak_ptr的出现就是为了解决shared_ptr的循环引用的问题的。以上文的例子来说,解决办法就是将两个类中的一个成员变量改为weak_ptr对象,比如将CB中的成员变量改为weak_ptr对象,即CB类的代码如下:

class CB {
  public:
    CB() {
      std::cout << "CB()" << std::endl;
    }
    ~CB() {
      std::cout << "~CB()" << std::endl;
    }
    void set_ptr(std::shared_ptr<CA>& ptr) {
      m_ptr_a = ptr;
    }
  private:
    std::weak_ptr<CA> m_ptr_a;
};

编译并运行结果,打印为:

CA()
CB()
1 2
~CA()
~CB()

通过这次结果可以看到,CA和CB的对象都被正常的析构了。
在这里插入图片描述
流程与上一例子大体相似,但是不同的是④这条引用是通过weak_ptr建立的,并不会增加引用计数。也就是说,CA的对象只有一个引用计数,而CB的对象只有2个引用计数,当main函数返回时,对象ptr_a和ptr_b被销毁,也就是①、③两条引用会被断开,此时CA对象的引用计数会减为0,对象被销毁,其内部的m_ptr_b成员变量也会被析构,导致CB对象的引用计数会减为0,对象被销毁,进而解决了引用成环的问题。

如果仔细看代码的话,会觉得很神奇!定义m_ptr_a修改成std::weak_ptr类型,但是set_ptr函数定义的参数还是std::shared_ptr类型。这个时候为什么没有报错?weak_ptr和shared_ptr的联系是什么呢?

weak_ptr的原理
weak_ptr是为了配合shared_ptr而引入的一种智能指针,它指向一个由shared_ptr管理的对象而不影响所指对象的生命周期,也就是,将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。不论是否有weak_ptr指向,一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。从这个角度看,weak_ptr更像是shared_ptr的一个助手而不是智能指针。

初始化方式
1 通过shared_ptr直接初始化,也可以通过隐式转换来构造;
2 允许移动构造,也允许拷贝构造。

#include <iostream>
#include <memory>

class Frame {};

int main()
{
  std::shared_ptr<Frame> f(new Frame());
  std::weak_ptr<Frame> f1(f);                     // shared_ptr直接构造
  std::weak_ptr<Frame> f2 = f;                    // 隐式转换
  std::weak_ptr<Frame> f3(f1);                    // 拷贝构造函数
  std::weak_ptr<Frame> f4 = f1;                   // 拷贝构造函数
  std::weak_ptr<Frame> f5;
  f5 = f;                                         // 拷贝赋值函数
  f5 = f2;                                        // 拷贝赋值函数
  std::cout << f.use_count() << std::endl;        // 1

  return 0;
}

需要注意,weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。

常用操作
w.user_count():返回weak_ptr的强引用计数;
w.reset(…):重置weak_ptr。
如何判断weak_ptr指向对象是否存在?

既然weak_ptr并不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。这时,就不能使用weak_ptr直接访问对象。那么如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁。

由于weak_ptr并没有重载operator ->和operator *操作符,因此不可直接通过weak_ptr使用对象,同时也没有提供get函数直接获取裸指针。典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值