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 对象构造, 它的构造和析构不会引起引用记数的增加或减少。
weak_ptr 是用来解决shared_ptr相互引用时的死锁问题,如果说两个shared_ptr相互引用,那么这两个指针的引 用计数永远不可能下降为0,资源永远不会释放。它是对对象的一种弱引用,不会增加对象的引用计数, 和shared_ptr之间可以相互转化,shared_ptr可以直接赋值给它,它可以通过调用lock函数来获得 shared_ptr。
weak_ptr没有重载操作符*
和->
,因为它不共享指针,不能操作资源,主要是为了通过shared_ptr获得资源的监测权,它的构造不会增加引用计数,它的析构也不会减少引用计数,纯粹只是作为一个旁观者来监视shared_ptr中管理的资源是否存在。weak_ptr还可以返回this
指针。
3.1 基本用法
-
通过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无效,资源已释放"; elsecout << "weak_ptr有效";
-
通过lock方法获取监视的shared_ptr,如:
std::weak_ptr<int> gw; void f() {if(gw.expired()) {cout << "gw无效,资源已释放";}else {auto spt = gw.lock(); // 尝试将 std::weak_ptr 提升为 std::shared_ptr。如果关联的 std::shared_ptr 已经释放(引用计数为 0),则返回一个空的 std::shared_ptr。cout << "gw有效, *spt = " << *spt << endl;} }int main() {{auto sp = atd::make_shared<int>(42);gw = sp;f();}f();return 0; }
3.2 weak_ptr返回this指针
shared_ptr
不能直接将this
指针返回shared_ptr
(会导致多个独立的 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返回,范例:
// weak_ptr_reutrn_this.cpp
#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(); // 安全:返回与现有 shared_ptr 共享引用计数的 shared_ptr}~A(){cout << "Deconstruction A" << endl;}
};int main()
{// auto spp = make_shared<A>();shared_ptr<A> sp1(new A);shared_ptr<A> sp2 = sp1->GetSelf(); // 安全:sp1 和 sp2 共享引用计数// shared_ptr<A> sp2;// {// shared_ptr<A> sp1(new A);// sp2 = sp1->GetSelf(); // ok// }cout << "sp1.use_count() = " << sp1.use_count()<< endl;cout << "sp2.use_count() = " << sp2.use_count()<< endl;return 0;
}
/*
sp1.use_count() = 2
sp2.use_count() = 2
Deconstruction A
*/
现在通过std::enable_shared_from_this
这个基类模板,安全地从对象内部获取一个 shared_ptr
。这样就不会导致一个对象的资源被多个shared_ptr
释放。
注意:获取自身智能指针的函数要在shared_ptr
的构造函数被调用之后才能使用,因为 enable_shared_from_this
内部的weak_ptr
只有通过shared_ptr
才能构造。
3.3 weak_ptr解决循环引用问题
shared_ptr
的智能指针循环引用的问题,因为智能指针的循环引用会导致内存泄漏,可以通过 weak_ptr解决该问题,只要将A或B的任意一个成员变量改为weak_ptr。例如:
#include <iostream>
#include <memory>
using namespace std;class A;
class B;class A {
public:std::weak_ptr<B> bptr; // 修改为weak_ptrint *val;A() {val = new int(1);}~A() {cout << "A is deleted" << endl;delete val;}
};class B {
public:std::shared_ptr<A> aptr;~B() {cout << "B is deleted" << endl;}
};//weak_ptr 是一种不控制对象生命周期的智能指针,
void test()
{std::shared_ptr<A> ap(new A);std::weak_ptr<A> wp1 = ap;std::weak_ptr<A> wp2 = ap;cout<< "ap.use_count()" << ap.use_count()<< endl;
}void test2()
{std::weak_ptr<A> wp;{std::shared_ptr<A> ap(new A);wp = ap;}cout<< "wp.use_count()" << wp.use_count() << ", wp.expired():" << wp.expired() << endl;if(!wp.expired()) {// wp不能直接操作对象的成员、方法std::shared_ptr<A> ptr = wp.lock(); // 需要先lock获取std::shared_ptr<A>*(ptr->val) = 20; }
}int main()
{test2();
// {
// std::shared_ptr<A> ap(new A);
// std::shared_ptr<B> bp(new B);
// ap->bptr = bp;
// bp->aptr = ap;
// }cout<< "main leave" << endl;return 0;
}
/*
A is deleted
wp.use_count()0, wp.expired():1
main leave
*/
这样在对B的成员赋值时,即执行bp->aptr=ap;时,由于aptr是weak_ptr,它并不会增加引用计数,所以ap的引用计数仍然会是1,在离开作用域之后,ap的引用计数为减为0,A指针会被析构,析构后其内部的bptr的引用计数会被减为1,然后在离开作用域后bp引用计数又从1减为0,B对象也被析构,不会发生内存泄漏。
3.4 weak_ptr
的使用注意事项
使用前需要检查是否合法,在使用weak_ptr
前需要调用wp.expired()函数判断一下。 因为weak_ptr
还仍旧存在,假如引用计数等于0,仍有某处“全局”性的存储块保存着这个计数信息。直到最后一个weak_ptr
对象被析构,这块“堆”存储块才能被回收。例如:
weak_ptr<int> wp; // 创建一个空的 weak_ptr
shared_ptr<int> sp_ok; // 创建一个空的 shared_ptr
{shared_ptr<int> sp(new int(1)); // 创建一个 shared_ptr,管理一个 int 对象wp = sp; // 将 weak_ptr 绑定到 shared_ptrsp_ok = wp.lock(); // 通过 weak_ptr 获取 shared_ptr
}
if (wp.expired()) {cout << "shared_ptr is destroy" << endl;
} else {cout << "shared_ptr no destroy" << endl;
}
// shared_ptr no destroy
在sp_ok = wp.lock();
当中由于 sp
仍然存在(引用计数为 1
),wp.lock()
成功返回一个 shared_ptr
,指向 sp
管理的对象。此时 sp_ok
的引用计数增加到 2
(sp
和 sp_ok
共享引用计数)。}
(作用域结束)后sp
超出作用域,它的引用计数递减到 1
(因为 sp_ok
仍然存在)于引用计数仍然为 1
,sp
管理的对象不会被释放。