一、介绍

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; //!
};

运行结果: 

五、总结

  1. weak_ptr虽然是一个模板类,但是不能用来直接定义指向原始指针的对象。
  2. weak_ptr接受shared_ptr类型的变量赋值,但是反过来是行不通的,需要使用lock函数。
  3. weak_ptr设计之初就是为了服务于shared_ptr的,所以不增加引用计数就是它的核心功能。
  4. 由于不知道什么之后weak_ptr所指向的对象就会被析构掉,所以使用之前请先使用expired函数检测一下。

参考:

智能指针weak_ptr的核心源码实现_week_ptr底层实现_dong_beijing的博客-CSDN博客

C++进阶:智能指针之weak_ptr - 掘金

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐