C++智能指针:weak_ptr

发布时间 2023-06-05 10:59:37作者: 韓さん

weak_ptr虽然是智能指针,但实际上是作为shared_ptr的辅助指针使用。
weak_ptr通常不单独使用,一般用于查看对应的shared_ptr的信息。weak_ptr没有重载*,->等指针运算符。
weak_ptr对象不会影响shared_ptr对象的引用计数。

 

#include<iostream>
#include<string.h>
#include<memory>
#include<stdio.h>
using namespace std;

int main()
{
  //创建空weak_ptr对象
  weak_ptr<double> w1;

  /*注意:不能使用nullptr构造weak_ptr对象,这一点与shared_ptr,unique_ptr都不同!*/
  weak_ptr<double> w1(nullptr); //错误
  //使用拷贝构造函数初始化weak_ptr对象
  weak_ptr<double> w2(w1);
  /*
    使用shared_ptr对象初始化weak_ptr对象
    注意:即使通过shared_ptr对象初始化weak_ptr对象,shared_ptr对象的引用计数也不会发生变化。
  */
  shared_ptr<double> s3(new int(6));
  weak_ptr<double> w3(s3);
  weak_ptr<double> w4 = s3;

  //注意:不能使用堆指针初始化weak_ptr对象
  weak_ptr<double> w5(new double); //错误

  //weak_ptr类只重载了=赋值运算符,可以把一个shared_ptr或weak_ptr对象赋给另一个weak_ptr对象。
  w4 = w3;
  return 0;
}

使用weak_ptr解决shared_ptr循环引用问题
循环引用现象
shared_ptr的循环引用,是因为shared_ptr对象虽然被销毁,但其引用计数没有变为0,导致开辟的内存没有被释放:

 

#include<iostream>
#include<string.h>
#include<memory>
#include<stdio.h>
using namespace std;

class A;
class B;

class A
{
    public:
        shared_ptr<B> bb; //解决循环引用:用weak_ptr代替
    public:
        A()
        {
          printf("A()\n");
        }
        ~A()
        {
          printf("~A()\n");
        }
};

class B
{
    public:
        shared_ptr<A> aa; //解决循环引用:用weak_ptr代替
    public:
        B()
        {
            printf("B()\n");
        }
        ~B()
        {
            printf("~B()\n");
        }
}
int main()
{
  shared_ptr<A> a(new A()); //a.use_count: 1
  shared_ptr<B> b(new B()); //b.use_count: 1

  a->bb = b; //b.use_count: 2
  b->aa = a; //a.use_count: 2
  return 0;
}

/*对象a,b生命周期结束时,b先销毁,b count=1,a再销毁,a count=1,于是a,b内部指针指向的内存没有被释放,造成内存泄漏*/

内存空间分析
细究一下上面代码的内存空间情况,首先通过shared_ptr a,b对象开辟了两个内存空间ma,mb以及对应的内部指针pa,pb,然后在pa指向的内存空间中通过b_存放了pb指针和空间mb,在pb指向的内存空间中通过a_存放了pa指针和空间ma,接着a,b对象分别被销毁,引用计数为1,因此指针pa,pb没有被销毁,内存空间ma,mb没有被释放。

可知,如果要解决循环引用的话,需要把其中一块内存空间手动释放,比如:
b->aa.reset();
或者,在智能指针赋值时只设置内部指针,不改变引用计数,也能解决循环引用。这就是weak_ptr的作用了。

weak_ptr解决循环引用
在类成员变量中使用weak_ptr,对象初始化时使用shared_ptr,就能够成功释放开辟的内存:
b->aa = a,a->bb=b没有影响引用计数,因此a,b销毁时,a,b内部指针指向的内存空间也被正常释放了。