C++中的shared_ptr和weak_ptr

发布时间 2023-03-27 20:58:15作者: weihao_ysgs

shared_ptr 和 weak_ptr

shared_ptr 相关特性

  • 引用计数,使用得当一般不会出现内存问题

  • 一般和 make_shared 结合使用,因为 shared_ptr 本身会有两个变量,一个是存储原先对象,一个是存储引用计数,一般这两个内存地区会离得比较远,make_shared 会尽量让两个近一点,为后期的销毁节约时间。

  • shared_ptr 出现的循环引用的问题。

#include <algorithm>
#include <iostream>
#include <memory>
#include <vector>

using namespace std;

struct ListNode
{
  int value;
  // std::weak_ptr<ListNode> next;
	std::shared_ptr<ListNode> next;
  ~ListNode() { std::cout << "~ListNode is called\n"; }
};

int main(int argc, char const *argv[])
{
  std::shared_ptr<ListNode> node1 = std::make_shared<ListNode>();  // node1.use_count() = 1
  std::shared_ptr<ListNode> node2 = std::make_shared<ListNode>();  // node2.use_count() = 1
  node1->next = node2; // node2.use_count() = 2
  node2->next = node1; // node1.use_count() = 2
  return 0;
}

上面的代码的输出结果为空,因为在销毁的时候,引用计数首先会 -1,然后看是否为 0 ,如果是,则会调用析构函数,没有则不会调用,这里我们很明显只会销毁一次,也就是在销毁对象的时候 ListNode 的析构函数不会被调用,因此会有内存泄漏的问题出现。但是这种写法其实对于一个链表来说又是经常会碰到的,怎么办呢?

  • Solution

解决办法就是将 ListNode 里的 shared_ptr 换成 weak_ptr,可以通过 shared_ptr 的类型对 weak_ptr 进行赋值,但此时并不会改变最初 shared_ptr的引用计数。输出结果为:

~ListNode is called
~ListNode is called
  • weak_ptr.lock()

weak_ptr指向shared_ptr指针指向的对象的内存,却并不拥有该内存。 但是,使用weak_ptr成员lock,则可返回其指向内存的一个shared_ptr对象,且在所指对象内存已经无效时,返回指针空值(nullptr)。由于weak_ptr是指向shared_ptr所指向的内存的,所以,weak_ptr并不能独立存在。

  • 示例 C++ 代码

    #include <algorithm>
    #include <iostream>
    #include <memory>
    #include <vector>
    
    using namespace std;
    
    struct ListNode
    {
      int value;
      std::weak_ptr<ListNode> next;
      // std::shared_ptr<ListNode> next;
      ~ListNode() { std::cout << "~ListNode is called\n"; }
    };
    
    int main(int argc, char const *argv[])
    {
      std::shared_ptr<ListNode> node1 =
          std::make_shared<ListNode>();  // node1.use_count() = 1
      std::shared_ptr<ListNode> node2 =
          std::make_shared<ListNode>();  // node2.use_count() = 1
      node1->next = node2;               // node2.use_count() = 2
      node2->next = node1;               // node1.use_count() = 2
    
      std::cout << "///////////////////////////////////////\n";
      auto node3 = std::make_shared<ListNode>();  // node3.use_count()  = 1
      std::cout << "node3.use_count(): " << node3.use_count() << std::endl;
      {
        auto node4 = std::make_shared<ListNode>();  // node4.use_count()  = 1
        std::cout << "node4.use_count(): " << node4.use_count() << std::endl;
        node4->next = node3;  // 这两个都是对 weak_ptr 赋值,所以不会增加引用计数
        std::cout << "node3.use_count(): " << node3.use_count() << std::endl;
        node3->next = node4;  // 这两个都是对 weak_ptr 赋值,所以不会增加引用计数
        std::cout << "node4.use_count(): " << node4.use_count() << std::endl;
      }
      if (auto temp_ptr = node3->next.lock(); temp_ptr)
      {
        std::cout << "Can access pointer\n";
      }
      else
      {
        std::cout << "Can not access pointer\n";
      }
    
      return 0;
    }
    

    输出:

    ///////////////////////////////////////
    node3.use_count(): 1
    node4.use_count(): 1
    node3.use_count(): 1
    node4.use_count(): 1
    ~ListNode is called
    Can not access pointer
    ~ListNode is called
    ~ListNode is called
    ~ListNode is called