shared_ptr

shared_ptr是一种共享型的智能指针,它使用一种“引用计数”的机制来让它变得“智能”。考虑这样一个过程:

  • 假设有一个叫pA的shared_ptr,创建它的时候引用计数为1;
  • 当有另一个pA2=pA时,查看pA和pA2的引用计数都变成了2;
  • 而当pA2离开函数作用域时,pA的计数又回到了1;
  • 当pA也离开自己的作用域时,计数则变为0,此时指针所指向的内存就会被销毁。

下面的例子中则体现了这个过程

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. struct A
  5. {
  6. string m_strA;
  7. A(string strA)
  8. {
  9. m_strA = strA;
  10. cout << "A(" << m_strA << ")" << endl;
  11. }
  12. ~A()
  13. {
  14. cout << "~A(" << m_strA << ")" << endl;
  15. }
  16. };
  17. void PrintSharedA(string strObj, shared_ptr<A>& pA)
  18. {
  19. cout << strObj << " 引用计数:" << pA.use_count() << endl;
  20. }
  21. void TestShared()
  22. {
  23. cout << "创建一个共享指针 pA" << endl;
  24. shared_ptr<A> pA = make_shared<A>("1");
  25. PrintSharedA("pA", pA);
  26. cout << "令pA2 = pA;" << endl;
  27. shared_ptr<A> pA2 = pA;
  28. PrintSharedA("pA", pA);
  29. PrintSharedA("pA2",pA2);
  30. }
  31. int main(int argc, char *argv[]) {
  32. TestShared();
  33. }

测试结果为:
image.png

unique_ptr

unique_ptr是一种独占型指针,相比于shared_ptr要轻量级许多,它即便没有引用计数的机制,但也可以在离开作用域后自动的销毁内存,因为它每次只允许一个指针实例拥有它(你可以把它想象成)。
假设我们定义了下面的一个结构:

  1. struct A
  2. {
  3. int m_iA;
  4. string m_strA;
  5. A(int iA, int strA)
  6. {
  7. m_iA = iA;
  8. m_strA = strA;
  9. }
  10. };

然后我们使用以下代码创建一个独占指针:

  1. unique_ptr<A> pA = make<A>(1, "1");//使用工厂方法创建指向A的独占指针

当我们想要向下面的代码这样将pA赋值给pB的时候,则会编译错误,因为独占指针不允许两个指针指向同一个实例

  1. unique_ptr<A> pA2 = pA;//错误,不能复制

想要实现类似的操作,你可以通过以下的方式“折衷”的完成

  1. unique_ptr<A> pA2 = std::move(pA); //将pA的所有权转移给pB,这时候你会发现pA变成了nullptr

shared_ptr的循环引用异常现象

考虑到下面这种异常情景:

  • 有一对成为了情侣的结构指针,它们叫pBoy和pGirl
  • 另外它们有着相互指向对方的指针,pBoy下有个pGirl的共享指针,pGirl下面也有个pBoy的共享指针
  • 这时当它们互相指向时,离开作用域后它们互相的引用计数并不会减少,导致无法析构

具体看下面这个例子:

  1. #include <iostream>
  2. #include <memory>
  3. using namespace std;
  4. struct Boy;
  5. struct Girl;
  6. struct Boy
  7. {
  8. shared_ptr<Girl> pGirl;
  9. string m_str;
  10. Boy(string str)
  11. {
  12. m_str = str;
  13. cout << "Boy(" << m_str << ")" << endl;
  14. }
  15. ~Boy()
  16. {
  17. cout << "~Boy(" << m_str << ")" << endl;
  18. }
  19. friend ostream &operator<<(ostream &output, const Boy& stuBoy)
  20. {
  21. output << "Boy(" << stuBoy.m_str << ")";
  22. return output;
  23. }
  24. };
  25. struct Girl
  26. {
  27. shared_ptr<Boy> pBoy;
  28. string m_str;
  29. Girl(string str)
  30. {
  31. m_str = str;
  32. cout << "Girl(" << m_str << ")" << endl;
  33. }
  34. ~Girl()
  35. {
  36. cout << "~Girl(" << m_str << ")" << endl;
  37. }
  38. friend ostream &operator<<(ostream &output, const Girl& stuGirl)
  39. {
  40. output << "stuGirl(" << stuGirl.m_str << ")";
  41. return output;
  42. }
  43. };
  44. template<class T>
  45. void PrintShared(string strObj, shared_ptr<T>& pT)
  46. {
  47. cout << strObj << " 引用计数:" << pT.use_count() << endl;
  48. }
  49. void TestShared2()
  50. {
  51. auto pBoy = make_shared<Boy>("boy");
  52. auto pGirl = make_shared<Girl>("girl");
  53. PrintShared("pBoy", pBoy);
  54. PrintShared("pGirl", pGirl);
  55. pBoy->pGirl = pGirl;
  56. PrintShared("pBoy", pBoy);
  57. PrintShared("pGirl", pGirl);
  58. pGirl->pBoy = pBoy;
  59. PrintShared("pBoy", pBoy);
  60. PrintShared("pGirl", pGirl);
  61. }
  62. int main(int argc, char *argv[]) {
  63. TestShared2();
  64. }

测试结果为:
image.png

weak_ptr

为了克服以上shared_ptr循环引用情况下的异常,weak_ptr则是被设计出来监视shared_ptr的,言下之意它并不是一个单独使用的指针,而是配合shared_ptr一起使用的。

参考:

  • 《深入应用C++11 代码优化与工程级应用》