日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 人文社科 > 生活经验 >内容正文

生活经验

C++智能指针: shared_ptr 实现详解

發布時間:2023/11/27 生活经验 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 C++智能指针: shared_ptr 实现详解 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

文章目錄

      • shared_ptr描述
        • 聲明
        • 作用
        • 原理實現
      • 函數使用
      • 關于shared_ptr循環引用問題

shared_ptr描述

聲明

shared_ptr屬于C++11特性中新加的一種智能指針,它的實現方式是模版類,頭文件<memory>
template <class T> class shared_ptr
所以使用shared_ptr的聲明方式即為
std::shared_ptr<type_id> statement 其中type_id為類型(可以是基本數據類型或者類),statement即為模版類對象

作用

shared_ptr 的理解如下:

  • 使用一種叫做RAII(Resource Acquisition Is Initialization)的技術作為實現基礎:
    在對象構造時獲取資源,接著控制對資源的訪問使之在對象的生命周期內始終保持有效,最后在對象析構的時候釋放資源
    raii技術的好處是:
    • 不需要顯式釋放資源
    • 對象所擁有的資源在其生命周期內始終有效
  • 防止忘記調用delete釋放內存或者程序異常退出時沒有釋放內存。
  • 同時它能夠將值語義轉為引用語義(即shared_ptr可以讓多個指針指向相同的對象,共享同一塊地址空間),shared_ptr使用引用技術方式來統計當前對象被引用的次數,每一次執行析構函數,引用計數就會-1,當引用計數減為0時自動刪除所指對象,回收對象空間。

原理實現

常用操作以及源碼實現如下:
類聲明如下

temple<typename T>
class SharedPtr {
public:... 
private:T *_ptr;int *_refCount;     //這里使用int型指針是為了保證拷貝構造時同一個地址空間的引用計數增加
};
  • constructor構造函數,初始化的時候默認引用計數為0
    SharedPtr() : _ptr((T *)0), _refCount(0)
    {}
    
    在使用普通指針初始化兩個shared_ptr的時候,兩個shared_ptr的引用計數都為1
    SharedPtr(T *obj) : _ptr(obj), _refCount(new int(1))
    {
    } 
    
    在進行拷貝構造的時候,使用shared_ptr去初始化另一個shared_ptr的時候引用計數會+1
    SharedPtr(SharedPtr &other) : _ptr(other._ptr), _refCount(&(++*other._refCount))
    {
    }
    
  • destructor析構函數,析構的時候在指針不為空且引用計數為0的時候釋放空間
    ~SharedPtr()
    {if (_ptr && --*_refCount == 0) {delete _ptr;delete _refCount;}
    }
    
  • operator = 當使用一個shared_ptr給另一個shared_ptr賦值的時候這里需要注意
    1. 由于指針指向發生變化,原來的_ptr指針的引用計數要–,且當達到了0的時候要注意回收原來指針的空間
    2. _ptr又指向了新的_ptr,則新的_ptr指針的引用計數要++
    SharedPtr &operator=(SharedPtr &other)
    {if(this==&other)return *this;//新指針引用計數要++  ++*other._refCount;//原指針引用計數要--,如果為0,則釋放空間if (--*_refCount == 0) {delete _ptr;delete _refCount;}//重新進行指向 _ptr = other._ptr;_refCount = other._refCount;return *this;
    }
    
  • operator* 解引用運算符,直接返回底層指針的引用,即共享的地址空間內容
    T &operator*()
    {if (_refCount == 0)return (T*)0;return *_ptr;
    }
    
  • operator ->指針運算符
    T *operator->()
    {if(_refCount == 0)return 0;return _ptr;
    }
    

函數使用

主要案例如下

  • 構造函數 constructor,std::shared_ptr初始化案例如下,以及對應的refcount打印
    #include <iostream>
    #include <memory>struct C {int* data;};int main () {std::shared_ptr<int> p1;//默認構造函數,refcount為0std::shared_ptr<int> p2 (nullptr);//使用一個空的對象初始化時refcount也為0//普通指針初始化是引用計數為1,p3,p4std::shared_ptr<int> p3 (new int);std::shared_ptr<int> p4 (new int, std::default_delete<int>());//擁有allocator的時候初始化同樣引用計數為1//但是緊接著又用改智能指針拷貝構造初始化其他智能指針p6,//所以最后其引用計數為2,p5std::shared_ptr<int> p5 (new int, [](int* p){delete p;}, std::allocator<int>());//這里p6本身為1,但是因為使用std::move去初始化p7,將p6指向轉給了p7//則p6智能指針recount--變為0,p7 ++由1變為2std::shared_ptr<int> p6 (p5);std::shared_ptr<int> p7 (std::move(p6));std::shared_ptr<int> p8 (std::unique_ptr<int>(new int));std::shared_ptr<C> obj (new C);std::shared_ptr<int> p9 (obj, obj->data);std::cout << "use_count:\n";std::cout << "p1: " << p1.use_count() << '\n';std::cout << "p2: " << p2.use_count() << '\n';std::cout << "p3: " << p3.use_count() << '\n';std::cout << "p4: " << p4.use_count() << '\n';std::cout << "p5: " << p5.use_count() << '\n';std::cout << "p6: " << p6.use_count() << '\n';std::cout << "p7: " << p7.use_count() << '\n';std::cout << "p8: " << p8.use_count() << '\n';std::cout << "p9: " << p9.use_count() << '\n';return 0;
    }
    
    輸出如下
    use_count:
    p1: 0
    p2: 0
    p3: 1
    p4: 1
    p5: 2
    p6: 0
    p7: 2
    p8: 1
    p9: 2
    
  • 析構函數
    // shared_ptr destructor example
    #include <iostream>
    #include <memory>int main () {auto deleter = [](int*p){std::cout << "[deleter called]\n"; delete p;};//使用特殊的delete函數去構造,析構的時候會執行改delete 中lamada表達式內容.即構造函數案例中的p5初始化方式std::shared_ptr<int> foo (new int,deleter);std::cout << "use_count: " << foo.use_count() << '\n';return 0;                        // [deleter called]
    }
    
    輸出如下
    use_count: 1
    [deleter called]
    
  • =賦值運算符
    // shared_ptr::operator= example
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> foo;std::shared_ptr<int> bar (new int(10));/*此時foo的引用計數為0,bar初始化后引用計數為1這里進行賦值操作,即foo的指向發生了變化,指向了bar1.foo引用計數--,因為已經為0了,此時直接釋放foo原來的空間2.bar引用計數++變為23.更改foo的引用計數和bar引用計數相等,并使得foo指向bar.因為他們共享同一個空間執行完之后fool和bar引用計數都相等,且解引用后數值都為0*/foo = bar;                          // copystd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '\n';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '\n';/*這里重新對bar進行了初始化,即原先的指向發生了更改,所以它的引用計數--,并且內容變為新的地址空間內容20foo繼續指向原先空間,但是內容并未變化。同時原先地址因為bar并不引用了,所以foo的引用計數--*/bar = std::make_shared<int> (20);   // movestd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '\n';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '\n';std::unique_ptr<int> unique (new int(30));foo = std::move(unique);            // move from unique_ptrstd::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '\n';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '\n';return 0;
    }
    
    輸出如下
    *foo: 10 foo.count 2
    *bar: 10 bar.count 2
    *foo: 10 foo.count 1
    *bar: 20 bar.count 1
    *foo: 30 foo.count 1
    *bar: 20 bar.count 1
    
  • shared_ptr::swap,交換兩個shared_ptr地址空間內容,但并不破壞各自引用計數
    
    // shared_ptr::swap example
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> foo (new int(10));std::shared_ptr<int> bar (new int(20));std::cout << "befor swap" << '\n';std::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '\n';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '\n';foo.swap(bar);std::cout << "after swap" << '\n';std::cout << "*foo: " << *foo << " foo.count " << foo.use_count() << '\n';std::cout << "*bar: " << *bar << " bar.count " << bar.use_count() << '\n';return 0;
    }
    
    輸出如下
    befor swap
    *foo: 10 foo.count 1
    *bar: 20 bar.count 1
    after swap
    *foo: 20 foo.count 1
    *bar: 10 bar.count 1
    
  • shared_ptr::reset 替換所管理的對象
    // shared_ptr::reset example
    #include <iostream>
    #include <memory>int main () {std::shared_ptr<int> sp;  // emptysp.reset (new int);       // 替換所管理對象,讓其更換地址指向std::cout << *sp << " " << sp.use_count() << " " << sp << '\n';*sp=10;std::cout << *sp << " " << sp.use_count() << " " << sp << '\n';sp.reset (new int);       // 清除上一個指針指向的內容,重新進行更換std::cout << *sp << " " << sp.use_count() << " " << sp << '\n';*sp=20;std::cout << *sp << " " << sp.use_count() << " " << sp << '\n';sp.reset();               // deletes managed object//std::cout << *sp << " " << sp.use_count() << '\n';return 0;
    }
    
    輸出如下,可以看到reset之后的地址發生了變化,即更改了指針的指向
    0 1 0x434cd50
    10 1 0x434cd50
    0 1 0x434cd90
    20 1 0x434cd90
    
  • shared_ptr::get獲取初始指針
    	// shared_ptr::get example
    #include <iostream>
    #include <memory>int main () {int* p = new int (10);std::shared_ptr<int> a (p);std::shared_ptr<int> b (new int(20));//此時a和p共享同一個地址空間,所以a和p的內容都為0,地址空間一樣if (a.get()==p)std::cout << "a and p point to the same location " << a << " " << p << '\n';std::cout << *a.get() << "\n";std::cout << *a << "\n";std::cout << *p << "\n";//此時a將共享空間釋放,重新更換指向b,但是*p為普通指針,并無法跟隨a更換指,所以p的地址內容變為0a=b;std::cout << "a and p after copy " << a << " " << b << '\n';// three ways of accessing the same address:std::cout << *a.get() << "\n";std::cout << *a << "\n";std::cout << *p << "\n";return 0;
    }
    
    輸出如下:
    a and p point to the same location 0x21cf2f0 0x21cf2f0
    10
    10
    10
    a and p after copy 0x21cf330 0x21cf330
    20
    20
    0
    

關于shared_ptr循環引用問題

循環引用是指兩個shared_ptr初始化之后相互指向,在函數作用域結束之后由于兩個指針都保持相互的指向,引用計數都為1,此時各自占用的內存空間無法釋放,最終產生內存泄露
舉例如下:

#include<iostream>  
#include<memory>  using namespace std;  class B;  
class A{  public:  shared_ptr<B> ptr_A;  ~A(){  cout << "refcount " << ptr_A.use_count() << '\n';cout<<"~A()"<<endl;  }  
};  
class B{  public:  //shared_ptr<A> ptr_B;//當采用shared_ptr指向A時會形成循環引用,則什么都不會輸出說明對象沒有被析構,可怕的內存泄露....  shared_ptr<A> ptr_B;//當采用弱引用時,避免了循環引用,有輸出,說明對象被析構了  ~B(){  cout << "refcount " << ptr_B.use_count() << '\n';cout<<"~B()"<<endl;  }  
};  
int main(){  shared_ptr<A> a(new A);  shared_ptr<B> b(new B);  a->ptr_A=b;  b->ptr_B=a;//若是循環引用:當a、b退出作用域的時候,A對象計數不為1(b保留了個計數呢),同理B的計數也不為1,那么對象將不會被銷毀,內存泄露了... cout << a.use_count() << " "  << b.use_count()<< endl;return 0;  
}  

輸出如下,可以看到釋放的之前兩個智能指針的引用計數都為2,析構的時候各自引用計數執行–到·1,最終無法釋放

2 2

將classB中的shared_ptr更改為weak_ptr即可成功釋放

#include<iostream>  
#include<memory>  using namespace std;  class B;  
class A{  public:  shared_ptr<B> ptr_A;  ~A(){  cout << "refcount " << ptr_A.use_count() << '\n';cout<<"~A()"<<endl;  }  
};  
class B{  public:  //shared_ptr<A> ptr_B;//當采用shared_ptr指向A時會形成循環引用,則什么都不會輸出說明對象沒有被析構,可怕的內存泄露....  weak_ptr<A> ptr_B;//當采用弱引用時,避免了循環引用,有輸出,說明對象被析構了  ~B(){  cout << "refcount " << ptr_B.use_count() << '\n';cout<<"~B()"<<endl;  }  
};  
int main(){  shared_ptr<A> a(new A);  shared_ptr<B> b(new B);  a->ptr_A=b;  b->ptr_B=a;//若是循環引用:當a、b退出作用域的時候,A對象計數不為1(b保留了個計數呢),同理B的計數也不為1,那么對象將不會被銷毀,內存泄露了...  return 0;  
}  

輸出如下,調用析構函數之前引一個智能指針的引用計已經將為1,執行析構之后即為0

1 2
refcount 1
~A()
refcount 0
~B()

參考文檔:
http://www.cplusplus.com/reference/memory/shared_ptr/
https://www.xuebuyuan.com/3190713.html

總結

以上是生活随笔為你收集整理的C++智能指针: shared_ptr 实现详解的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。