如何:创建和使用 weak_ptr 实例

有时,对象必须存储一种方法来访问 shared_ptr 的基础对象,而不会导致引用计数递增。 通常,在 shared_ptr 实例之间有循环引用时,会出现这种情况。

最佳设计是尽量避免指针的共享所有权。 但是,如果必须拥有 shared_ptr 实例的共享所有权,请避免它们之间的循环引用。 如果循环引用不可避免,甚至出于某种原因甚至更可取,请使用 weak_ptr 为一个或多个所有者提供对另一个 shared_ptr 所有者的弱引用。 通过使用 weak_ptr,可以创建一个联接到现有相关实例集的 shared_ptr,但前提是基础内存资源仍然有效。 weak_ptr 本身不参与引用计数,因此,它无法阻止引用计数变为零。 但是,可以使用 weak_ptr 尝试获取初始化该副本的 shared_ptr 的新副本。 若已删除内存,则 weak_ptr 的 bool 运算符返回 false。 若内存仍然有效,则新的共享指针会递增引用计数,并保证只要 shared_ptr 变量保留在作用域内,内存就会有效。

示例

下面的代码示例演示的案例是将 weak_ptr 用于确保正确删除具有循环依赖项的对象。 在检查此示例时,假设仅在考虑替代解决方案后创建该示例。 这些 Controller 对象表示计算机进程的一些方面,它们独立运行。 每个控制器必须随时能够查询其他控制器的状态,每个控制器都包含一个专用 vector<weak_ptr<Controller>>,用于实现此目的。 每个向量都包含一个循环引用,因此,使用 weak_ptr 实例而不是 shared_ptr

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

using namespace std;

class Controller
{
public:
   int Num;
   wstring Status;
   vector<weak_ptr<Controller>> others;
   explicit Controller(int i) : Num(i), Status(L"On")
   {
      wcout << L"Creating Controller" << Num << endl;
   }

   ~Controller()
   {
      wcout << L"Destroying Controller" << Num << endl;
   }

   // Demonstrates how to test whether the
   // pointed-to memory still exists or not.
   void CheckStatuses() const
   {
      for_each(others.begin(), others.end(), [](weak_ptr<Controller> wp) {
         auto p = wp.lock();
         if (p)
         {
            wcout << L"Status of " << p->Num << " = " << p->Status << endl;
         }
         else
         {
            wcout << L"Null object" << endl;
         }
      });
   }
};

void RunTest()
{
   vector<shared_ptr<Controller>> v{
       make_shared<Controller>(0),
       make_shared<Controller>(1),
       make_shared<Controller>(2),
       make_shared<Controller>(3),
       make_shared<Controller>(4),
   };

   // Each controller depends on all others not being deleted.
   // Give each controller a pointer to all the others.
   for (int i = 0; i < v.size(); ++i)
   {
      for_each(v.begin(), v.end(), [&v, i](shared_ptr<Controller> p) {
         if (p->Num != i)
         {
            v[i]->others.push_back(weak_ptr<Controller>(p));
            wcout << L"push_back to v[" << i << "]: " << p->Num << endl;
         }
      });
   }

   for_each(v.begin(), v.end(), [](shared_ptr<Controller> &p) {
      wcout << L"use_count = " << p.use_count() << endl;
      p->CheckStatuses();
   });
}

int main()
{
   RunTest();
   wcout << L"Press any key" << endl;
   char ch;
   cin.getline(&ch, 1);
}
Creating Controller0
Creating Controller1
Creating Controller2
Creating Controller3
Creating Controller4
push_back to v[0]: 1
push_back to v[0]: 2
push_back to v[0]: 3
push_back to v[0]: 4
push_back to v[1]: 0
push_back to v[1]: 2
push_back to v[1]: 3
push_back to v[1]: 4
push_back to v[2]: 0
push_back to v[2]: 1
push_back to v[2]: 3
push_back to v[2]: 4
push_back to v[3]: 0
push_back to v[3]: 1
push_back to v[3]: 2
push_back to v[3]: 4
push_back to v[4]: 0
push_back to v[4]: 1
push_back to v[4]: 2
push_back to v[4]: 3
use_count = 1
Status of 1 = On
Status of 2 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 2 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 3 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 2 = On
Status of 4 = On
use_count = 1
Status of 0 = On
Status of 1 = On
Status of 2 = On
Status of 3 = On
Destroying Controller0
Destroying Controller1
Destroying Controller2
Destroying Controller3
Destroying Controller4
Press any key

作为试验,请将向量 others 修改为一个 vector<shared_ptr<Controller>>,然后在输出中,请注意返回时 RunTest 不会调用析构函数。

另请参阅

智能指针(现代 C++)