栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

weak

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

weak

智能指针weak_ptr
  • weak_ptr基本用法
    • 循环引用问题
    • weak_ptr定义
  • weak_ptr解决循环引用
  • weak_ptr函数
  • weak_ptr返回this指针
    • 通过shared_ptr返回this指针
    • shared_from_this()
  • weak_ptr使用注意事项

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叫弱引用指针。是一种不控制对象生命周期的智能指针, 它指向一个 shared_ptr 管理的对象。weak_ptr 设计的目的是为了协助shared_ptr而引入的一种智能指针,它可以解决shared_ptr循环引用的问题。weak_ptr只可以从一个shared_ptr或另一个 weak_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_ptr sp(new int(10));
weak_ptr wp(sp);
cout << wp.use_count() << endl; //输出1

2.expired():判断所观察资源是否已经释放:

shared_ptr sp(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_ptr gw;
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不改变其所共享的shared_ptr实例的引用计数,那就可能存在weak_ptr指向的对象被释放掉这种情况。
这时就不能使用weak_ptr直接访问对象。那么可以用上面的代码首先判断weak_ptr指向对象是否存在?

weak_ptr返回this指针 通过shared_ptr返回this指针

不要将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将会被构造的两个智能指针各自析构,导致重复析构的错误。

shared_from_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;
}
  • 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_ptr在使用前需要检查合法性。

weak_ptr wp;
{
	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服务期高级架构系统教程学习

我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号