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内部指针指向的内存空间也被正常释放了。