• std::shared_ptr(基础、仿写、安全性)


    目录

    一、c++参考手册

    1、解释说明 

     2、代码示例

    3、运行结果

    二、对std::shared_ptr分析

    1、shared_ptr基础

    2、创建shared_ptr实例

    3、访问所指对象

    4、拷贝和赋值操作

    5、检查引用计数

    三、仿写std::shared_ptr代码

    1、单一对象

    2、数组对象

    四、shared_ptr遇到问题

    1、shared_ptr在多线程中遇到的问题

    (1)、shared_ptr 的数据结构

     (2)、有三个shared_ptr对象

     3、其他问题:

    4、相互引用(weak_ptr)


    一、c++参考手册

    1、解释说明 

     2、代码示例

    1. #include
    2. #include
    3. #include
    4. #include
    5. #include
    6. struct Base
    7. {
    8. Base() { std::cout << " Base::Base()\n"; }
    9. // 注意:此处非虚析构函数 OK
    10. ~Base() { std::cout << " Base::~Base()\n"; }
    11. };
    12. struct Derived: public Base
    13. {
    14. Derived() { std::cout << " Derived::Derived()\n"; }
    15. ~Derived() { std::cout << " Derived::~Derived()\n"; }
    16. };
    17. void thr(std::shared_ptr p)
    18. {
    19. std::this_thread::sleep_for(std::chrono::seconds(1));
    20. std::shared_ptr lp = p; // 线程安全,虽然自增共享的 use_count
    21. {
    22. static std::mutex io_mutex;
    23. std::lock_guard lk(io_mutex);
    24. std::cout << "local pointer in a thread:\n"
    25. << " lp.get() = " << lp.get()
    26. << ", lp.use_count() = " << lp.use_count() << '\n';
    27. }
    28. }
    29. int main()
    30. {
    31. std::shared_ptr p = std::make_shared();
    32. std::cout << "Created a shared Derived (as a pointer to Base)\n"
    33. << " p.get() = " << p.get()
    34. << ", p.use_count() = " << p.use_count() << '\n';
    35. std::thread t1(thr, p), t2(thr, p), t3(thr, p);
    36. p.reset(); // 从 main 释放所有权
    37. std::cout << "Shared ownership between 3 threads and released\n"
    38. << "ownership from main:\n"
    39. << " p.get() = " << p.get()
    40. << ", p.use_count() = " << p.use_count() << '\n';
    41. t1.join(); t2.join(); t3.join();
    42. std::cout << "All threads completed, the last one deleted Derived\n";
    43. }

    3、运行结果

    二、对std::shared_ptr分析

    1、shared_ptr基础

    shared_ptr是一个引用计数智能指针,用于共享对象的所有有权,也就是说可以多个指针指向一个对象

    1. class Object {
    2. private:
    3. int value;
    4. public:
    5. Object(int x = 0) :value(x) { cout << "Constructor Object ..." << endl; }
    6. ~Object() { cout << "Destroy Object ..." << endl; }
    7. int& Value() { return value; }
    8. const int& Value()const { return value; }
    9. };
    10. int main() {
    11. shared_ptr pObj(new Object(100));
    12. cout << (*pObj).Value() << endl;
    13. cout << "pObj 引用计数:" << pObj.use_count() << endl;
    14. shared_ptrpObj2 = pObj;
    15. cout << "pObj 引用计数:" << pObj.use_count() << endl;
    16. cout << "pObj 引用计数:" << pObj2.use_count() << endl;
    17. return 0;
    18. }
    19. 从上面这段代码中,我们对shared_ptr指针有了一些直观的了解。
      一方面,跟STL中大多数容器类型一样, shared_ptr 也是模板类,因此在创建shared. ptr时需要指定其指向的类型。另一方面,正如其名一样,shared_ptr 指针允许让多个该类型的指针共享同一堆分配对象。同时shared_ptr 使用经典的“引用计数"方法来管理对象资源,每个shared_ptr对象关联一个共享的引用计数。
      对于shared_ ptr 在拷贝和赋值时的行为是,每个shared _ptr 都有一个关联的计数值, 通常称为引用计数。无论何时我们拷贝一个shared_ptr,计数器都会递增。
      例如,当用一个shared_ptr 初始化另一个 shred_ptr,或将它当做参数传递给一个函数以及作为函数的返回值时,它所关联的计数器就会递增。
      当我们给shared_ ptr 赋予一个新值或是shared ptr 被销毁(例如一个局部的shared_ ptr离开其作用域)时,计数器就会递减。shared_ ptr 对象的计数器变为0,它就会自动释放自己所管理的对象。
      对比我们上面的代码可以看到:当我们将一个指向Object对象的指针交给pObj管理后,其关联的引用计数为1。接下来,我们用pObj初始化pObj2,两者关联的引用计数值增加为2。随后,函数结束, pObj 和PObj2相继离开函数作用域,相应的引用计数值分别自减1最后变为0,于是Object对象被自动释放(调用其析构函数)。

      2、创建shared_ptr实例

      最安全和高效的方法是调用make_shared库函数,该函数会在堆中分配一个对象并初始化, 最后返回指向此对象的share_ptr实例。如果你不想使用make_shared,也可以先明确new出一个对象, 然后把其原始指针传递给share_ptr 的构造函数。

      1. int main() {
      2. shared_ptr ptr = make_shared(10,'s');
      3. cout << *ptr << endl;
      4. int* p = new int(10);
      5. shared_ptr<int> pInt(p);
      6. cout << *pInt << endl;
      7. }

      3、访问所指对象

      shared_ptr的使用方式与普通指针的使用方式类似,既可以使用解引用操作符*获得原始对象进而访问其各个成员,也可以使用指针访问符->来访问原始对象的各个成员。

      4、拷贝和赋值操作

      我们可以用一个shared_ptr对象来初始化另一个share_ptr 实例,该操作会增加其引用计数值。

      1. int main() {
      2. shared_ptr pStr = make_shared(10, 's');
      3. cout << pStr.use_count() << endl;
      4. shared_ptr pStr2(pStr);
      5. cout << pStr.use_count() << endl;
      6. cout << pStr2.use_count() << endl;
      7. }

       如果sharedp_tr实例p和另一个shared_ptr 实例q所指向的类型相同或者可以相互转换,我们还可以进行诸如p =q这样赋值操作。该操作会递减p的引用计数值,递增q的引用计数值。

      1. class Object {
      2. private:
      3. string value;
      4. public:
      5. Object(string x = "") :value(x) { cout << value << "Create Object.." << endl; }
      6. ~Object() { cout << value << "Destroy Object.." << endl; }
      7. string& Value() { return value; }
      8. const string& Value() const { return value; }
      9. };
      10. int main() {
      11. shared_ptr pObj = make_shared("a.tex");;
      12. shared_ptr pObj1 = make_shared("b.text");
      13. cout << pObj.use_count() << endl;
      14. cout << pObj1.use_count() << endl;
      15. pObj = pObj1;
      16. cout << pObj.use_count()<< endl;
      17. cout << pObj1.use_count() << endl;
      18. return 0;
      19. }
      20. 5、检查引用计数

        shared_ptr提供两个检查共享引用计数值,分别是unique()和use_cout();

        • use_cout()效率比较低,适合测试和调试;
        • unique()返回的是true和false,检测是否原始指针的唯一拥有者。

        三、仿写std::shared_ptr代码

        1、单一对象

        1. #include
        2. template <class _Ty>
        3. class MyDeletor
        4. {
        5. public:
        6. MyDeletor() = default;
        7. void operator ()(_Ty* ptr)const
        8. {
        9. if (ptr != nullptr)
        10. {
        11. delete ptr;
        12. }
        13. }
        14. };
        15. template <class _Ty>
        16. class RefCnt
        17. {
        18. public:
        19. _Ty* mptr;
        20. std::atomic_int ref;
        21. public:
        22. RefCnt(_Ty* p = nullptr) :mptr(p), ref(mptr != nullptr) {}
        23. ~RefCnt() {}
        24. };
        25. template<class _Ty,class _Dx=MyDeletor<_Ty>>
        26. class my_shared_ptr
        27. {
        28. private:
        29. RefCnt<_Ty>* ptr;
        30. _Dx mDeletor;
        31. public:
        32. my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
        33. {
        34. if (p != nullptr)
        35. {
        36. ptr = new RefCnt(p);
        37. }
        38. }
        39. my_shared_ptr(const my_shared_ptr& _Y) :ptr(_Y.ptr)
        40. {
        41. if (ptr != nullptr)
        42. {
        43. ptr->ref += 1;
        44. }
        45. }
        46. my_shared_ptr(my_shared_ptr&& _Y) :ptr(_Y.ptr)
        47. {
        48. _Y.ptr = nullptr;
        49. }
        50. operator bool()const { return ptr != nullptr; }
        51. my_shared_ptr& operator=(const my_shared_ptr& _Y)
        52. {
        53. if (this == &_Y || this->ptr == _Y.ptr)return this;
        54. if (ptr != nullptr && --ptr->ref == 0)
        55. {
        56. mDeletor(ptr);
        57. }
        58. ptr = _Y.ptr;
        59. if (ptr != nullptr)
        60. {
        61. ptr->ref += 1;
        62. }
        63. return *this;
        64. }
        65. my_shared_ptr& operator=(my_shared_ptr&& _Y)
        66. {
        67. if (this == &_Y)return this;
        68. if (this->ptr == _Y.ptr && this->ptr != nullptr && _Y.ptr != nullptr)
        69. {
        70. this->ptr->ref -= 1;
        71. _Y.ptr = nullptr;
        72. return *this;
        73. }
        74. if (this->ptr != nullptr && --ptr->ref == 0)
        75. {
        76. mDeletor(ptr);
        77. }
        78. ptr = _Y.ptr;
        79. _Y.ptr = nullptr;
        80. return *this;
        81. }
        82. void reset(_Ty* p = nullptr)
        83. {
        84. if (this->ptr != nullptr && --this->ptr->ref == 0)
        85. {
        86. mDeletor(ptr);
        87. }
        88. ptr = new RefCnt<_Ty>(p);
        89. }
        90. ~my_shared_ptr()
        91. {
        92. if (this->ptr != nullptr && --this->ptr->ref == 0)
        93. {
        94. mDeletor(ptr->mptr);
        95. delete ptr;
        96. }
        97. ptr = nullptr;
        98. }
        99. _Ty* get()const
        100. {
        101. return ptr->mptr;
        102. }
        103. _Ty& operator*()const
        104. {
        105. return *get();
        106. }
        107. _Ty* operator ->()const
        108. {
        109. return get();
        110. }
        111. size_t use_count()const
        112. {
        113. if (this->ptr == nullptr)return 0;
        114. return this->ptr->ref;
        115. }
        116. void swap(my_shared_ptr* r)
        117. {
        118. std::swap(this->ptr, r->ptr);
        119. }
        120. };

        2、数组对象

        1. template <class _Ty>
        2. class MyDeletor<_Ty[]>
        3. {
        4. public:
        5. MyDeletor() = default;
        6. void operator ()(_Ty* ptr)const
        7. {
        8. if (ptr != nullptr)
        9. {
        10. delete []ptr;
        11. }
        12. }
        13. };
        14. template <class _Ty>
        15. class RefCnt
        16. {
        17. public:
        18. _Ty* mptr;
        19. std::atomic_int ref;
        20. public:
        21. RefCnt(_Ty* p = nullptr) :mptr(p), ref(mptr != nullptr) {}
        22. ~RefCnt() {}
        23. };
        24. template<class _Ty, class _Dx >
        25. class my_shared_ptr<_Ty[],_Dx>
        26. {
        27. private:
        28. RefCnt<_Ty>* ptr;
        29. _Dx mDeletor;
        30. public:
        31. my_shared_ptr(_Ty* p = nullptr) :ptr(nullptr)
        32. {
        33. if (p != nullptr)
        34. {
        35. ptr = new RefCnt(p);
        36. }
        37. }
        38. my_shared_ptr(const my_shared_ptr& _Y) :ptr(_Y.ptr)
        39. {
        40. if (ptr != nullptr)
        41. {
        42. ptr->ref += 1;
        43. }
        44. }
        45. my_shared_ptr(my_shared_ptr&& _Y) :ptr(_Y.ptr)
        46. {
        47. _Y.ptr = nullptr;
        48. }
        49. operator bool()const { return ptr != nullptr; }
        50. my_shared_ptr& operator=(const my_shared_ptr& _Y)
        51. {
        52. if (this == &_Y || this->ptr == _Y.ptr)return this;
        53. if (ptr != nullptr && --ptr->ref == 0)
        54. {
        55. mDeletor(ptr);
        56. }
        57. ptr = _Y.ptr;
        58. if (ptr != nullptr)
        59. {
        60. ptr->ref += 1;
        61. }
        62. return *this;
        63. }
        64. my_shared_ptr& operator=(my_shared_ptr&& _Y)
        65. {
        66. if (this == &_Y)return this;
        67. if (this->ptr == _Y.ptr && this->ptr != nullptr && _Y.ptr != nullptr)
        68. {
        69. this->ptr->ref -= 1;
        70. _Y.ptr = nullptr;
        71. return *this;
        72. }
        73. if (this->ptr != nullptr && --ptr->ref == 0)
        74. {
        75. mDeletor(ptr);
        76. }
        77. ptr = _Y.ptr;
        78. _Y.ptr = nullptr;
        79. return *this;
        80. }
        81. void reset(_Ty* p = nullptr)
        82. {
        83. if (this->ptr != nullptr && --this->ptr->ref == 0)
        84. {
        85. mDeletor(ptr);
        86. }
        87. ptr = new RefCnt<_Ty>(p);
        88. }
        89. ~my_shared_ptr()
        90. {
        91. if (this->ptr != nullptr && --this->ptr->ref == 0)
        92. {
        93. mDeletor(ptr->mptr);
        94. delete ptr;
        95. }
        96. ptr = nullptr;
        97. }
        98. _Ty* get()const
        99. {
        100. return ptr->mptr;
        101. }
        102. _Ty& operator*()const
        103. {
        104. return *get();
        105. }
        106. _Ty* operator ->()const
        107. {
        108. return get();
        109. }
        110. size_t use_count()const
        111. {
        112. if (this->ptr == nullptr)return 0;
        113. return this->ptr->ref;
        114. }
        115. void swap(my_shared_ptr* r)
        116. {
        117. std::swap(this->ptr, r->ptr);
        118. }
        119. _Ty& operator[](const int idx)const
        120. {
        121. return ptr->mptr[idx];
        122. }
        123. };

        四、shared_ptr遇到问题

        1、shared_ptr在多线程中遇到的问题

        • (1) (shared_ptr)的引用计数本身是线程安全(引用计数是原子操作)。
        • (2) 多个线程同时读同一个shared_ptr对象是线程安全的。
        • (3)如果是多个线程对同一个shared_ptr对象进行读和写,则需要加锁。
        • (4)多线程读写shared_ptr 所指向的同一个对象,不管是相同的shared_ptr对象,还是不同的shared_ptr对象,也需要加锁保护。

        具体分析一下为什么“因为shared_ptr有两个数据成员,读写操作不能原子化”使得多线程读写同一个shared_ptr 对象需要加锁。

        (1)、shared_ptr 的数据结构

        shared_ptr 是引用计数型(reference counting)智能指针,几乎所有的实现都采用在堆(heap)上放个计数值(count)。

        1. class Object {
        2. private:
        3. int value;
        4. public:
        5. Object(int x=0):value(x){}
        6. ~Object() {}
        7. int& Value() { return value; }
        8. const int& Value() const { return value; }
        9. };
        10. int main() {
        11. shared_ptr apa(new Object(10));
        12. shared_ptr apb = apa;
        13. return 0;
        14. }
        15.  (2)、有三个shared_ptr对象

           3、其他问题:

          shared_ptr 作为unordered_map的key如果把sharedp_tr放到unordered_set中,或者用于unordered_map 的key,那么要小心hash table 退化为链表。
          但是其hash_value是shared_ptr隐式转换为bool的结果。也就是说,如果不自定义hash函数,那么
          unordered_ {set/map} 会退化为链表。
          为什么要尽量使用make_shared?(申请被管理对象以及引用计数的内存;调用适当的构造函数初始化对象;返回一个shared_ptr为了节省一次内存分配,原来shared_ptr x(new Object (10) );需要为Object对象和RefCnt各分配一次内存,现在用make_ shared() 的话,可以一次分配一 块足够大的内存,供Object 和RefCnt对象容身。不过Object的构造函数所需的参数要传给make _shared后者再传给Object:: Object(),这只有在C++11里通过perfect forwarding(完美转发)才能完美解决。

          4、相互引用(weak_ptr

          下一篇

        16. 相关阅读:
          VMware Explore 大会发布重磅云上技术之外,VMware 有哪些前沿探索?
          传述最详细的干货,让简历面试不再成为你找工作的绊脚石
          Revit插件实现【快速扣减】的两种操作
          mysql数据库 - 统诉
          10.私网与公网的转换---NAT网络地址转换技术
          信号量机制的实现
          基于51单片机的电子密码锁设计
          10个设计人士应该关注的国内外资源网站
          pytorh模型训练、测试
          hmcl_HMCL安装与使用
        17. 原文地址:https://blog.csdn.net/weixin_48560325/article/details/128032383