1.3 weak_ptr
- share_ptr虽然已经很好用了,但是有一点share_ptr智能指针还是有内存泄露的情况,当两个对象相互
使用一个shared_ptr成员变量指向对方,会造成循环引用,使引用计数失效,从而导致内存泄漏。 - weak_ptr 是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象. 进行该对象的内
存管理的是那个强引用的shared_ptr, weak_ptr只是提供了对管理对象的一个访问手段。 - weak_ptr 设计的目的是为配合 shared_ptr 而引入的一种智能指针来协助 shared_ptr 工作, 它只可以从
一个 shared_ptr 或另一个 weak_ptr 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
1.3.1 weak_ptr基本用法
- 通过
use_count()
方法获取当前观察资源的引用计数,如下所示:
shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
cout << wp.use_count() << endl; //结果讲输出1
- 通过
expired()
方法判断所观察资源是否已经释放,如下所示:
shared_ptr<int> sp(new int(10));
weak_ptr<int> wp(sp);
if(wp.expired())
cout << "weak_ptr无效,资源已释放";
else
cout << "weak_ptr有效";
- 通过
lock()
方法获取监视的shared_ptr,并且如果资源还在的情况会锁住资源(如果其他的智能指针要释放资源的话),且必须要先lock()
主资源再用expired()
判断资源释放已经释放,如下所示:
std::weak_ptr<int> gw;
void f()
{
//锁住资源
// 1. 资源还在
// 2. 资源已经释放
auto spt = gw.lock();
if(gw.expired()) {
cout << "gw无效,资源已释放";
}
else {
cout << "gw有效, *spt = " << *spt << endl;
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
return 0;
}
1.3.2 weak_ptr返回this指针
shared_ptr章节中提到不能直接将this指针返shared_ptr,需要通过派生std::enable_shared_from_this
类,并通过其方法shared_from_this
来返回指针,原因是std::enable_shared_from_this类中有一个weak_ptr
,这个weak_ptr用来观察this智能指针,调用shared_from_this()方法是,会调用内部这个weak_ptr的lock()
方法,将所观察的shared_ptr返回,再看前面的范例
#include <iostream>
#include <memory>
using namespace std;
class A: public std::enable_shared_from_this<A>
{
public:
shared_ptr<A>GetSelf()
{
return shared_from_this(); //
}
~A()
{
cout << "Destructor A" << endl;
}
};
int main()
{
shared_ptr<A> sp1(new A);
shared_ptr<A> sp2 = sp1->GetSelf(); // ok
return 0;
}
1.3.3 weak_ptr解决循环引用问题
weak_ptr是一种用于解决shared_ptr相互引用时产生死锁问题的智能指针。如果有两个shared_ptr相互引用,那么这两个shared_ptr指针的引用计数永远不会下降为0,资源永远不会释放。weak_ptr是对对象的一种弱引用,它不会增加对象的use_count,weak_ptr和shared_ptr可以相互转化,shared_ptr可以直接赋值给weak_ptr,weak_ptr也可以通过调用lock函数来获得shared_ptr。
- weak_ptr指针通常不单独使用,只能和 shared_ptr 类型指针搭配使用。将一个weak_ptr绑定到一个shared_ptr不会改变shared_ptr的引用计数。一旦最后一个指向对象的shared_ptr被销毁,对象就会被释放。即使有weak_ptr指向对象,对象也还是会被释放。
- weak_ptr并没有重载
operator->
和operator *
操作符,因此不可直接通过weak_ptr使用对象,典型的用法是调用其lock函数来获得shared_ptr示例,进而访问原始对象。
#include <iostream>
#include <memory>
using namespace std;
class A;
class B;
class A {
public:
std::weak_ptr<B> bptr; // 修改为weak_ptr
~A() {
cout << "A is deleted" << endl;
}
};
class B {
public:
std::shared_ptr<A> aptr;
~B() {
cout << "B is deleted" << endl;
}
};
int main()
{
{
std::shared_ptr<A> ap(new A);
std::shared_ptr<B> bp(new B);
ap->bptr = bp;//由于bptr是weak_ptr类型,所以bp的引用计数不会+1,故bp.use_count() = 1
bp->aptr = ap;//而ap的引用计数此时为ap.use_count() = 2
}
//bp出作用域的时候引用计数-1 为0 故而释放资源, 同时bp->aptr也会释放使得 ap的引用计数减1 为 1
//ap本身释放资源使得计数减1 为0 最终释放资源
cout<< "main leave" << endl;
return 0;
}