C++智能指针weak_ptr详解
一、背景
weak_ptr
这个指针是在C++11的时候引入的标准库,它的出现完全是为了弥补shared_ptr
天生有缺陷的问题,其实相比于上一代的智能指针auto_ptr
来说,shared_ptr
可以说近乎完美,但由于是通过引用计数实现的它,虽然解决了指针独占的问题,但也引来了引用成环的问题,这种问题靠它自己是没办法解决的,所以在C++11的时候将shared_ptr
和weak_ptr
一起引入了标准库,用来解决循环引用的问题。
二、使用方法
std::weak_ptr
是一种智能指针,它对被 std::shared_ptr 管理的对象存在非拥有性(“弱”)引用。在访问所引用的对象前必须先转换为 std::shared_ptr。
std::weak_ptr
用来表达临时所有权的概念:当某个对象只有存在时才需要被访问,而且随时可能被他人删除时,可以使用 std::weak_ptr
来跟踪该对象。需要获得临时所有权时,则将其转换为 std::shared_ptr,此时如果原来的 std::shared_ptr 被销毁,则该对象的生命期将被延长至这个临时的 std::shared_ptr 同样被销毁为止。
三、实例
1.循环引用的例子
class BB;
class AA
{
public:
AA() { cout << "AA::AA() called" << endl; }
~AA() { cout << "AA::~AA() called" << endl; }
shared_ptr<BB> m_bb_ptr; //!
};
class BB
{
public:
BB() { cout << "BB::BB() called" << endl; }
~BB() { cout << "BB::~BB() called" << endl; }
shared_ptr<AA> m_aa_ptr; //!
};
int main()
{
shared_ptr<AA> ptr_a(new AA);
shared_ptr<BB> ptr_b(new BB);
cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
//下面两句导致了AA与BB的循环引用,结果就是AA和BB对象都不会析构
ptr_a->m_bb_ptr = ptr_b;
ptr_b->m_aa_ptr = ptr_a;
cout << "ptr_a use_count: " << ptr_a.use_count() << endl;
cout << "ptr_b use_count: " << ptr_b.use_count() << endl;
return 0;
}
结果
可以看到由于AA和BB内部的shared_ptr各自保存了对方的一次引用,所以导致了ptr_a和ptr_b销毁的时候都认为内部保存的指针计数没有变成0,所以AA和BB的析构函数不会被调用。解决方法就是把一个shared_ptr替换成weak_ptr。
class BB;
class AA
{
public:
AA() { cout << "AA::AA() called" << endl; }
~AA() { cout << "AA::~AA() called" << endl; }
weak_ptr<BB> m_bb_ptr; //!
};
class BB
{
public:
BB() { cout << "BB::BB() called" << endl; }
~BB() { cout << "BB::~BB() called" << endl; }
shared_ptr<AA> m_aa_ptr; //!
};
运行结果:
2.通过锁来保证指针的有效性的例子
std::weak_ptr<int> gw;
void f()
{
if (auto spt = gw.lock()) { // 使用之前必须复制到 shared_ptr
std::cout << *spt << "\n";
}
else {
std::cout << "gw is expired\n";
}
}
int main()
{
{
auto sp = std::make_shared<int>(42);
gw = sp;
f();
}
f();
return 0;
}
四、总结
-
weak_ptr
虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。 -
weak_ptr
接受shared_ptr
类型的变量赋值,但是反过来是行不通的,需要使用lock
函数。 -
weak_ptr
设计之初就是为了服务于shared_ptr
的,所以不增加引用计数就是它的核心功能。 - 由于不知道什么之后
weak_ptr
所指向的对象就会被析构掉,所以使用之前请先使用expired
函数检测一下。
参考:
上一篇: Spring Boot实现文件上传
下一篇: Spring Boot文件上传与下载实战