
C++智能指针weak_ptr详解
一、介绍
std::weak_ptr
是一种智能指针,通常不单独使用,只能和 shared_ptr 类型指针搭配使用,可以视为 shared_ptr 指针的一种辅助工具。借助 weak_ptr 类型指针可以获取 shared_ptr 指针的一些状态信息,比如有多少指向相同的 shared_ptr 指针、通过expired()
判断shared_ptr 指针指向的堆内存是否已经被释放等等,还可以解决shared_ptr 循环引用的问题。
二、内部函数
std::weak_ptr
的成员函数如下:
三、std::weak_ptr用法
weak_ptr可以从一个shared_ptr或者另一个weak_ptr对象构造,获得资源的观测权。但weak_ptr没有共享资源,它的构造不会引起指针引用计数的增加。使用weak_ptr的成员函数use_count()
可以观测资源的引用计数,另一个成员函数expired()
的功能等价于use_count()==0
,但更快。表示被观测的资源(也就是shared_ptr的管理的资源)已经不复存在。
#include <iostream>
#include <memory>
int main() {
{
std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
std::cout << sh_ptr.use_count() << std::endl; // 输出1
std::weak_ptr<int> wp(sh_ptr);
std::cout << wp.use_count() << std::endl; // 赋值给weak_ptr后还是输出1
if(!wp.expired()){ // 检查sh_ptr是否还有效
std::shared_ptr<int> sh_ptr2 = wp.lock(); //将sh_ptr赋值给sh_ptr2
*sh_ptr = 100;
std::cout << wp.use_count() << std::endl; // 输出2
}
} //delete memory
std::weak_ptr<int> wp;
{
std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);
wp = sh_ptr;
std::cout << std::boolalpha << wp.expired() << std::endl; // 输出false,引用对象还没删除
} //delete memory
std::cout << std::boolalpha << wp.expired() << std::endl; // 输出true,引用对象已经删除
return 0;
}
打印
weak_ptr可以使用一个非常重要的成员函数lock()
从被观测的shared_ptr获得一个可用的shared_ptr对象, 从而操作资源。但当expired()==true
的时候,lock()
函数将返回一个存储空指针的shared_ptr。
#include <iostream>
#include <memory>
int main(int argc, const char* argv[]) {
std::shared_ptr<int> sp(new int(10));
std::weak_ptr<int> wp(sp);
//sp.reset();
if (std::shared_ptr<int> pa = wp.lock()) {
std::cout << *pa << std::endl;
}
else {
std::cout << "wp指向对象为空" << std::endl;
}
sp.reset();
if (std::shared_ptr<int> pa = wp.lock()) {
std::cout << *pa << std::endl;
}
else {
std::cout << "wp指向对象为空" << std::endl;
}
return 0;
}
打印
四、循环引用问题
weak_ptr的一个作用是解决share_ptr的循环引用问题。如下面代码所示,class AA中含有指向class BB的shared指针, class BB 中含有指向class AA的shared指针,这样形成了循环引用。m_bb_ptr和m_aa_ptr的强引用计数永远大于等于1,所以直到程序退出前都不会被退出,这种情况有时候在正常的业务逻辑中是不可避免的,而解决循环引用的方法是改用weak_ptr:
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; //!
};
运行结果:
五、总结
weak_ptr
虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。weak_ptr
接受shared_ptr
类型的变量赋值,但是反过来是行不通的,需要使用lock
函数。weak_ptr
设计之初就是为了服务于shared_ptr
的,所以不增加引用计数就是它的核心功能。- 由于不知道什么之后
weak_ptr
所指向的对象就会被析构掉,所以使用之前请先使用expired
函数检测一下。
参考:
智能指针weak_ptr的核心源码实现_week_ptr底层实现_dong_beijing的博客-CSDN博客
更多推荐








所有评论(0)