- weak_ptr基本用法
- 循环引用问题
- weak_ptr定义
- weak_ptr解决循环引用
- weak_ptr函数
- weak_ptr返回this指针
- 通过shared_ptr返回this指针
- shared_from_this()
- weak_ptr使用注意事项
虽然shared_ptr是用来避免内存泄漏,可以自动释放内存。但是shared_ptr在使用中可能存在循环引用,使引用计数失效,从而导致内存泄漏的情况。如下代码所示:
#include#include using namespace std; class A { public: std::shared_ptr bptr; ~A() { cout << "A is deleted" << endl; } }; class B { public: std::shared_ptr aptr; ~B() { cout << "B is deleted" << endl; } }; int main() { {//设定一个作用域 std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; } cout<< "main leave" << endl; // 循环引用导致ap bp退出了作用域都没有析构 return 0; }
循环引用导致ap和bp的引用计数都为2,在离开作用域之后,ap和bp的引用计数只减为1,而没有减为0,导致两个指针都不会被析构,产生内存泄漏。
weak_ptr定义weak_ptr解决循环引用weak_ptr叫弱引用指针。是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。weak_ptr 设计的目的是为了协助shared_ptr而引入的一种智能指针,它可以解决shared_ptr循环引用的问题。weak_ptr只可以从一个shared_ptr或另一个 weak_ptr 对象来构造, 它的构造和析构不会引起引用记数的增加或减少。
shared_ptr智能指针的循环引用导致的内存泄漏问题,可以通过weak_ptr解决。只需要将A或B的任意一个成员变量改为weak_ptr:
#include#include using namespace std; class A { public: std::weak_ptr bptr; // 修改为weak_ptr ~A() { cout << "A is deleted" << endl; } }; class B { public: std::shared_ptr aptr; ~B() { cout << "B is deleted" << endl; } }; int main() { {//设定一个作用域 std::shared_ptr ap(new A); std::shared_ptr bp(new B); ap->bptr = bp; bp->aptr = ap; } cout<< "main leave" << endl; return 0; }
上面代码中在对B的成员赋值时,即执行ap->bptr=bp时,由于bptr是weak_ptr,它并不会增加引用计数,所以bp的引用计数仍然会是1,在离开作用域之后,bp的引用计数为减为0,A指针会被析构,析构后其内部的aptr的引用计数会被减为1,然后在离开作用域后bp引用计数又从1减为0,B对象也被析构,不会发生内存泄漏。
weak_ptr函数1.use_count():获取当前观察资源的引用计数:
shared_ptrsp(new int(10)); weak_ptr wp(sp); cout << wp.use_count() << endl; //输出1
2.expired():判断所观察资源是否已经释放:
shared_ptrsp(new int(10)); weak_ptr wp(sp); if(wp.expired()) cout << "weak_ptr无效,资源已释放"; else cout << "weak_ptr有效";
3.lock():lock()函数返回一个指向共享对象的shared_ptr,如果对象被释放,则返回一个空shared_ptr:
std::weak_ptrgw; void f() { auto spt = gw.lock(); if(gw.expired()) { cout << "gw无效,资源已释放"; } else { cout << "gw有效, *spt = " << *spt << endl; } } int main() { { auto sp = std::make_shared (42); gw = sp; f(); } f(); return 0; }
weak_ptr返回this指针 通过shared_ptr返回this指针使用场景:weak_ptr不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。
这时就不能使用weak_ptr直接访问对象。那么可以用上面的代码首先判断weak_ptr指向对象是否存在?
不要将this指针作为shared_ptr返回出来,因为this指针本质上是一个裸指针。因此,返回this可能会导致重复析构:
#include#include using namespace std; class A { public: shared_ptr GetSelf() { return shared_ptr(this); // 不要这么做 } ~A() { cout << "Destructor A" << endl; } }; int main() { shared_ptr sp1(new A); shared_ptr sp2 = sp1->GetSelf(); return 0; }
运行结果:
由于用同一个指针(this)构造了两个智能指针sp1和sp2,而他们之间是没有任何关系的,在离开作用域之后this将会被构造的两个智能指针各自析构,导致重复析构的错误。
正确返回this的shared_ptr的做法是:让目标类通过std::enable_shared_from_this类,然后使用基类的成员函数shared_from_this()来返回this的shared_ptr:
#include#include using namespace std; class A: public std::enable_shared_from_this { public: shared_ptrGetSelf() { return shared_from_this(); // } ~A() { cout << "Destructor A" << endl; } }; int main() { shared_ptr sp1(new A); shared_ptr sp2 = sp1->GetSelf(); // ok return 0; }
weak_ptr使用注意事项
- std::enable_shared_from_this类中有一个weak_ptr,这个weak_ptr用来观察this智能指针,调用shared_from_this()方法是,会调用内部这个weak_ptr的lock()方法,将所观察的shared_ptr返回。
- 在外面创建A对象的智能指针和通过对象返回this的智能指针都是安全的,因为shared_from_this()是内部的weak_ptr调用lock()方法之后返回的智能指针,在离开作用域之后,sp1的引用计数减为0,A对象会被析构,不会出现A对象被析构两次的问题。
- 需要注意的是,获取自身智能指针的函数应在shared_ptr的构造函数被调用之后才能使用,因为enable_shared_from_this内部的weak_ptr只有通过shared_ptr才能构造。
weak_ptr在使用前需要检查合法性。
weak_ptrwp; { shared_ptr sp(new int(1)); //sp.use_count()==1 wp = sp; //wp不会改变引用计数,所以sp.use_count()==1 shared_ptr sp_ok = wp.lock(); // } shared_ptr sp_null = wp.lock(); //sp_null.use_count()==0;
因为上述代码中sp和sp_ok离开了作用域,所以其容纳的对象已经被释放了。
所以最后得到了一个容纳NULL指针的sp_null对象。故在使用wp前需要调用wp.expired()函数判断一下。
如果shared_ptr sp_ok和weak_ptr wp属于同一个作用域:
#include#include using namespace std; int main() { weak_ptr wp; shared_ptr sp_ok; { shared_ptr sp(new int(1)); //sp.use_count()==1 wp = sp; //wp不会改变引用计数,所以sp.use_count()==1 sp_ok = wp.lock(); } if(wp.expired()) { cout << "shared_ptr is destroy" << endl; } else { cout << "shared_ptr no destroy" << endl; } }
运行结果:
这时wp不为空。
文章参考与<零声教育>的C/C++linux服务期高级架构系统教程学习



