C++ Boost weak_ptr智能指针超详细讲解

一、提要

在 C++11 中,boost::weak_ptr是另一类智能指针,一般是用COM组件生成、调用,本文阐述这种指针的特点和用法。

二、特别智能指针(Special Smart Pointers)

到目前为止介绍的每个智能指针都可以在不同的场景中单独使用。但是,boost::weak_ptr 仅在与 boost::shared_ptr 结合使用时才有意义。 boost::weak_ptr 在 boost/weak_ptr.hpp 中定义。

示例1.Usingboost::weak_ptr

#include <boost/shared_ptr.hpp>
#include <boost/weak_ptr.hpp>
#include <thread>
#include <functional>
#include <iostream>
void reset(boost::shared_ptr<int> &sh)
{
  sh.reset();
}
void print(boost::weak_ptr<int> &w)
{
  boost::shared_ptr<int> sh = w.lock();
  if (sh)
    std::cout << *sh << '\n';
}
int main()
{
  boost::shared_ptr<int> sh{new int{99}};
  boost::weak_ptr<int> w{sh};
  std::thread t1{reset, std::ref(sh)};
  std::thread t2{print, std::ref(w)};
  t1.join();
  t2.join();
}

boost::weak_ptr 必须使用 boost::shared_ptr 进行初始化。它最重要的成员函数是 lock()。 lock() 返回一个 boost::shared_ptr ,它与用于初始化弱指针的共享指针共享所有权。如果共享指针为空,则返回的指针也将为空。

boost::weak_ptr 是有意义的,只要一个函数需要与一个由共享指针管理的对象一起工作,但对象的生命周期不依赖于函数本身。该函数只能使用该对象,只要它由程序中其他位置的至少一个共享指针拥有。如果共享指针被重置,则对象无法保持活动状态,因为相应函数内有一个额外的共享指针。

示例 1 在 main() 中创建了两个线程。第一个线程执行函数 reset(),该函数接收对共享指针的引用。第二个线程执行函数 print(),该函数接收对弱指针的引用。这个弱指针之前已经用共享指针初始化了。

程序启动后,reset() 和 print() 会同时执行。但是,无法预测执行顺序。这会导致当对象被 print() 访问时,reset() 会破坏该对象的潜在问题。

弱指针通过以下方式解决了这个问题:调用 lock() 返回一个共享指针,该指针指向一个有效对象(如果在调用时存在一个有效对象)。如果不是,则共享指针设置为 0,相当于一个空指针。

boost::weak_ptr 本身对对象的生命周期没有任何影响。为了安全地访问 print() 函数中的对象,lock() 返回一个 boost::shared_ptr。这保证了即使不同的线程尝试释放对象,由于返回的共享指针,它仍将继续存在。

示例2 .使用boost::intrusive_ptr

#include <boost/intrusive_ptr.hpp>
#include <atlbase.h>
#include <iostream>
void intrusive_ptr_add_ref(IDispatch *p) { p->AddRef(); }
void intrusive_ptr_release(IDispatch *p) { p->Release(); }
void check_windows_folder()
{
  CLSID clsid;
  CLSIDFromProgID(CComBSTR{"Scripting.FileSystemObject"}, &clsid);
  void *p;
  CoCreateInstance(clsid, 0, CLSCTX_INPROC_SERVER, __uuidof(IDispatch), &p);
  boost::intrusive_ptr<IDispatch> disp{static_cast<IDispatch*>(p), false};
  CComDispatchDriver dd{disp.get()};
  CComVariant arg{"C:\\Windows"};
  CComVariant ret{false};
  dd.Invoke1(CComBSTR{"FolderExists"}, &arg, &ret);
  std::cout << std::boolalpha << (ret.boolVal != 0) << '\n';
}
int main()
{
  CoInitialize(0);
  check_windows_folder();
  CoUninitialize();
}

通常,boost::intrusive_ptr 的工作方式与 boost::shared_ptr 相同。但是,虽然 boost::shared_ptr 会跟踪引用特定对象的共享指针的数量,但开发人员在使用 boost::intrusive_ptr 时必须这样做。如果其他类已经跟踪引用,这可能是有意义的。

boost::intrusive_ptr 在 boost/intrusive_ptr.hpp 中定义。

示例 2 使用 COM 提供的功能,因此只能在 Windows 上构建和运行。 COM 对象是 boost::intrusive_ptr 的一个很好的例子,因为它们跟踪引用它们的指针的数量。内部引用计数器可以通过成员函数 AddRef() 和 Release() 递增或递减 1。一旦计数器达到 0,COM 对象就会自动销毁。

​​​ 从 intrusive_ptr_add_ref() 和 intrusive_ptr_release() 调用两个成员函数 AddRef() 和 Release()。 Boost.Intrusive 希望开发人员定义这两个函数,当引用计数器必须递增或递减时,它们会自动调用。传递给这些函数的参数是指向用于实例化类模板 boost::intrusive_ptr 的类型的指针。

此示例中使用的 COM 对象称为 FileSystemObject,默认情况下可在 Windows 上使用。它提供对底层文件系统的访问,例如,检查给定目录是否存在。在示例 1.9 中,检查了名为 C:\Windows 的目录是否存在。其内部工作方式仅取决于 COM,与 boost::intrusive_ptr 的功能无关。关键是一旦侵入指针 disp 在 check_windows_folder() 结束时超出范围,函数 intrusive_ptr_release() 就会被自动调用。这反过来会将 FileSystemObject 的内部引用计数器减为 0 并销毁该对象。

传递给 boost::intrusive_ptr 的构造函数的参数 false 会阻止 intrusive_ptr_add_ref() 被调用。当使用 CoCreateInstance() 创建 COM 对象时,计数器已设置为 1。因此,不得使用 intrusive_ptr_add_ref() 递增。

原文地址:https://yamagota.blog.csdn.net/article/details/127045971