这是一篇关于讲解关于weak_ptr使用的问题,在c++11被引入,weak_ptr也是c++11智能指针的一种,是为了解决shared_ptr循环引用的问题而引入。

1. 循环引用导致的问题

循环引用,简单来说就是:两个对象互相使用一个shared_ptr成员变量指向对方的会造成循环引用,导致引用计数失效。
````c++
#include <iostream>
#include <memory>

class classB;
class classA {
public:
classA() {
std::cout << "classA()" << std::endl;
}
~classA() {
std::cout << "~classA()" << std::endl;
}
void set_ptr(std::shared_ptr<classB>& ptr) {
std::cout << "在类A中,拷贝之前m_ptr_b的引用次数为" << m_ptr_b.use_count() << std::endl;
m_ptr_b = ptr;
std::cout << "在类A中,拷贝之后m_ptr_b的引用次数为" << m_ptr_b.use_count() << std::endl;
}
private:
std::shared_ptr<classB> m_ptr_b;
};

class classB {
public:
classB() {
std::cout << "classB()" << std::endl;
}
~classB() {
std::cout << "~classB()" << std::endl;
}
void set_ptr(std::shared_ptr<classA>& ptr) {
std::cout << "在类B中,拷贝之前m_ptr_a的引用次数为" << m_ptr_a.use_count() << std::endl;
m_ptr_a = ptr;
std::cout << "在类B中,拷贝之后m_ptr_a的引用次数为" << m_ptr_a.use_count() << std::endl;
}
private:
std::shared_ptr<classA> m_ptr_a;
};

int main()
{
std::shared_ptr<classA> ptr_a(new classA()); //调用类A构造函数,此时ptr_a的引用计数已经加1
std::cout << "new 完之后 ptr_a的引用计数为" << ptr_a.use_count() << std::endl;
std::shared_ptr<classB> ptr_b(new classB()); //调用类B构造函数,此时ptr_b的引用计数已经加1
std::cout << "new 完之后 ptr_b的引用计数为" << ptr_b.use_count() << std::endl;
ptr_a->set_ptr(ptr_b); //set内部会发生拷贝动作,导致智能指针的引用计数加1,
ptr_b->set_ptr(ptr_a);
std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;
system("pause");
return 0;
}

> 可以看到,上述代码执行完之后,没有执行类A和类B的析构函数。与代码分析类似,由于引用计数的值在退出main函数之前没有减到1,因此也没执行对象的析构函数。
## 2.使用weak_ptr解决循环引用不析构的问题
> 将上述代码中类A和类B成员中的智能指针换为weak_ptr可以解决,如下代码
````c++
#include <iostream>
#include <memory>

class classB;
class classA {
public:
    classA() {
        std::cout << "classA()" << std::endl;
    }
    ~classA() {
        std::cout << "~classA()" << std::endl;
    }
    void set_ptr(std::shared_ptr<classB>& ptr) {
        std::cout << "在类A中,拷贝之前m_ptr_b的引用次数为" << m_ptr_b.use_count() << std::endl;
        m_ptr_b = ptr;
        std::cout << "在类A中,拷贝之后m_ptr_b的引用次数为" << m_ptr_b.use_count() << std::endl;
    }
private:
    std::weak_ptr<classB> m_ptr_b;
};

class classB {
public:
    classB() {
        std::cout << "classB()" << std::endl;
    }
    ~classB() {
        std::cout << "~classB()" << std::endl;
    }
    void set_ptr(std::shared_ptr<classA>& ptr) {
        std::cout << "在类B中,拷贝之前m_ptr_a的引用次数为" << m_ptr_a.use_count() << std::endl;
        m_ptr_a = ptr;
        std::cout << "在类B中,拷贝之后m_ptr_a的引用次数为" << m_ptr_a.use_count() << std::endl;
    }
private:
    std::weak_ptr<classA> m_ptr_a;
};

int main()
{
    std::shared_ptr<classA> ptr_a(new classA());  //调用类A构造函数,此时ptr_a的引用计数已经加1
    std::cout << "new 完之后 ptr_a的引用计数为" << ptr_a.use_count() << std::endl;
    std::shared_ptr<classB> ptr_b(new classB());    //调用类B构造函数,此时ptr_b的引用计数已经加1
    std::cout << "new 完之后 ptr_b的引用计数为" << ptr_b.use_count() << std::endl;
    ptr_a->set_ptr(ptr_b);  //set内部会发生拷贝动作,导致智能指针的引用计数加1,
    ptr_b->set_ptr(ptr_a);
    std::cout << ptr_a.use_count() << " " << ptr_b.use_count() << std::endl;
    system("pause");
    return 0;
}

- [] 使用weak_ptr建立的引用不会导致引用计数的增加

3.weak_ptr的原理

weak_ptr只是指向shared_ptr对象的一个指针,它可以从一个shared_ptr或者weak_ptr构造而来,但是weak_ptr的构造和析构不会引起shared_ptr的引用计数的增减。weak_ptr是shared_ptr的一个助手。

4.使用weak_ptr需要注意

weak_ptr并不改变其所共享的shared_ptr实例的引用计数,可能存在weak_ptr指向的对象被释放掉这种情况。这时,就不能使用weak_ptr直接访问对象。那么如何判断weak_ptr指向对象是否存在呢?C++中提供了lock函数来实现该功能。如果对象存在,lock()函数返回一个指向共享对象的shared_ptr(引用计数会增1),否则返回一个空shared_ptr。weak_ptr还提供了expired()函数来判断所指对象是否已经被销毁.