2.7. 弱指针

到目前为止介绍的各种智能指针都能在不同的场合下独立使用。 相反,弱指针只有在配合共享指针一起使用时才有意义。 弱指针 boost::weak_ptr 的定义在 boost/weak_ptr.hpp 里。

  1. #include <windows.h>
  2. #include <boost/shared_ptr.hpp>
  3. #include <boost/weak_ptr.hpp>
  4. #include <iostream>
  5.  
  6. DWORD WINAPI reset(LPVOID p)
  7. {
  8. boost::shared_ptr<int> *sh = static_cast<boost::shared_ptr<int>*>(p);
  9. sh->reset();
  10. return 0;
  11. }
  12.  
  13. DWORD WINAPI print(LPVOID p)
  14. {
  15. boost::weak_ptr<int> *w = static_cast<boost::weak_ptr<int>*>(p);
  16. boost::shared_ptr<int> sh = w->lock();
  17. if (sh)
  18. std::cout << *sh << std::endl;
  19. return 0;
  20. }
  21.  
  22. int main()
  23. {
  24. boost::shared_ptr<int> sh(new int(99));
  25. boost::weak_ptr<int> w(sh);
  26. HANDLE threads[2];
  27. threads[0] = CreateThread(0, 0, reset, &sh, 0, 0);
  28. threads[1] = CreateThread(0, 0, print, &w, 0, 0);
  29. WaitForMultipleObjects(2, threads, TRUE, INFINITE);
  30. }

boost::weak_ptr 必定总是通过 boost::shared_ptr 来初始化的。一旦初始化之后,它基本上只提供一个有用的方法: lock()。此方法返回的boost::shared_ptr 与用来初始化弱指针的共享指针共享所有权。 如果这个共享指针不含有任何对象,返回的共享指针也将是空的。

当函数需要一个由共享指针所管理的对象,而这个对象的生存期又不依赖于这个函数时,就可以使用弱指针。 只要程序中还有一个共享指针掌管着这个对象,函数就可以使用该对象。 如果共享指针复位了,就算函数里能得到一个共享指针,对象也不存在了。

上例的 main() 函数中,通过 Windows API 创建了2个线程。 于是乎,该例只能在 Windows 平台上编译运行。

第一个线程函数 reset() 的参数是一个共享指针的地址。 第二个线程函数 print() 的参数是一个弱指针的地址。 这个弱指针是之前通过共享指针初始化的。

一旦程序启动之后,reset()print() 就都开始执行了。 不过执行顺序是不确定的。 这就导致了一个潜在的问题:reset() 线程在销毁对象的时候print() 线程可能正在访问它。

通过调用弱指针的 lock() 函数可以解决这个问题:如果对象存在,那么 lock() 函数返回的共享指针指向这个合法的对象。否则,返回的共享指针被设置为0,这等价于标准的null指针。

弱指针本身对于对象的生存期没有任何影响。 lock() 返回一个共享指针,print() 函数就可以安全的访问对象了。 这就保证了——即使另一个线程要释放对象——由于我们有返回的共享指针,对象依然存在。