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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

c++11:智能指针

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

我們在程序運行的過程中,經常出現段錯誤、內存持續增大等,是C++顯式內存管理存在的問題,主要歸納為以下幾點:

  • 野指針:一些內存單元已經釋放,但之前指向它的指針還在使用。
  • 重復釋放:程序試圖釋放已經被釋放過的內存單元。
  • 內存泄漏:沒有釋放不再使用的內存單元。
  • 緩沖區溢出:數組越界。
  • 不配對的new[]/delete
  • 針對以上1~3的問題,C++標準中提供了智能指針來解決。
    智能指針是基于RAII(Resource Acquisition Is Initialization)機制實現的類(模板),具有指針的行為(重載了operator*與operator->操作符)。當對象創建的時候,進行初始化;離開其作用域后,通過自動調用析構函數釋放資源。

    C++98中,智能指針通過一個模板類型"auto_ptr"來實現,auto_ptr對象通過初始化指向由new創建的動態內存,當auto_ptr對象生命周期結
    束時,其析構函數會將auto_ptr對象擁有的動態內存自動釋放。即使發生異常,通過異常的棧展開過程也能將動態內存釋放。但其有一些缺點:

    • 賦值和拷貝操作的目標對象會先釋放其原來所擁有的對象
      auto_ptr<int> ap1(new int(100)); ????auto_ptr<int> ap2(ap1); ????// ap1 == nullptr;

      auto_ptr 不能用在stl容器中,因為stl容器要求存儲的類型必須為值語義類型。即兩個對象在拷貝或賦值之后相等(ap1 == ap2)

    • auto_ptr 析構的時候調用的是delete,所以不能用auto_ptr管理數組指針
      ~auto_ptr() { delete _M_ptr; }

    因此,在C++11標準中,改用unique_ptr、shared_ptr及weak_ptr等智能指針來對動態內存進行管理。auto_ptr為了兼容以前的代碼被遺留下來,不建議使用。

    頭文件

    <memory>

    命名空間為

    ?std

    unique_ptr

    概念:
    • unique_ptr“唯一”擁有其所指對象,同一時刻只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現std::move())。* unique_ptr“唯一”擁有其所指對象,同一時刻只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現std::move())。
    • unique_ptr指針本身的生命周期:從unique_ptr指針創建時開始,直到離開作用域。離開作用域時,若其指向對象,則將其所指對象銷毀(默認使用delete操作符,用戶可指定其他操作)。
    • unique_ptr指針與其所指對象的關系:在智能指針生命周期內,可以改變智能指針所指對象,如創建智能指針時通過構造函數指定、通過reset方法重新指定、通過release方法釋放所有權、通過移動語義轉移所有權。
    基本用法:
    #include <iostream> #include <memory> #include <vector> using namespace std; struct Foo { ????Foo() {} ????~Foo() {} ????void Print() { cout << "Foo" << endl; } }; int main(void) { ????Foo* p1 = new Foo(); ????unique_ptr<Foo> up1;?????? // up1==nullptr //? up1 = p1;????????????????? // 編譯錯誤,不支持這樣賦值 ????up1.reset(p1);???????????? // 替換管理對象,并釋放之前管理的對象 ????p1 = nullptr; //? unique_ptr<Foo> up2(up1);? // 編譯錯誤,不支持這樣構造 ????unique_ptr<Foo> up2(std::move(up1)); // up1所有權轉移到up2。up1==nullptr ????up1.swap(up2);???????????? // up2與up1管理對象的指針交換。 up2==nullptr ????if (up1) {???????????????? // up1 != nullptr ????????up1->Print();????????? // unique_ptr重載了-> ????????(*up1).Print();??????? // unique_ptr重載了* ????} //? up2->Print();????????????? // 錯誤 up2 == nullptr, 必須先判斷再調用 ????p1 = up1.get();??????????? // get() 返回所管理對象的指針, up1繼續持有其管理權 ????p1 = up1.release();??????? // release() 返回管理對象的指針,并釋放管理權,up1==nullptr ????delete p1; ????unique_ptr<Foo> up3(new Foo()); ????up3.reset();??????????????? // 顯示釋放釋放管理對象的內存,也可以這樣做:up = nullptr; ????vector<unique_ptr<Foo>> v; ????unique_ptr<Foo> up4(new Foo()); //? v.push_back(up4);??????????? // 編譯錯誤,不支持這樣拷貝 ????v.push_back(std::move(up4);? // 只能up4放棄對其所有權,通過std::move()將所有權轉移到容器中 ????return 0; }
    應用場景:
    • 只要unique_ptr智能指針創建成功,其析構都會被調用,確保動態資源的釋放------避免內存泄漏。
    • 把unique_ptr作為引用參數,傳遞給其他例程,不用擔心該指針在例程中被copy一份,或不小心釋放掉。

    shared_ptr

    概念:
    • shared_ptr 基于“引用計數”模型實現, 多個shared_ptr對象可以擁有同一個動態對象,并維護了一個共享的引用計數。當最后一個指向該對象的shared_ptr被銷毀或者reset時,會自動釋放其所指的對象,回收動態資源。
    • 銷毀該對象時,使用默認的delete/delete[]表達式,或者是在構造 shared_ptr 時傳入的自定義刪除器(deleter),以實現個性化的資源釋放動作。
    基本用法:
    #include <iostream> #include <memory> #include <vector> #include <assert.h> using namespace std; struct Foo { ????int v; }; int main(void) { ????shared_ptr<Foo> sp1(new Foo{10}); ????cout << sp1.unique() << endl;??? // 1 當前shared_ptr唯一擁有Foo管理權時,返回true,否則返回false ????cout << sp1.use_count() << endl; // 1 返回當前對象的引用計數 ????shared_ptr<Foo> sp2(sp1); ????assert(sp1->v == sp2->v);??????? // sp1與sp2共同擁有Foo對象 ????cout << sp2.unique() << endl;??? // 0 false ????cout << sp2.use_count() << endl; // 2 ????sp1.reset();???????????????????? // 釋放對Foo的管理權,同時引用計數減1 ????assert(sp1 == nullptr);????????? // sp1 為空 ????cout << sp1.unique() << endl;??? // 0 不會拋出異常 ????cout << sp1.use_count() << endl; // 0 不會拋出異常 ????cout << sp1.get() << endl;?????? // 0 不會跑出異常 //? cout << sp1->v << endl;????????? // 執行錯誤 sp1為nullptr時,operator* 和 operator-> 都會導致未定義行為 ????cout << sp2.unique() << endl;??? // 1 true ????sp1.swap(sp2);?????????????????? // sp1與sp2交換管理權,及引用計數 ????assert(sp2 == nullptr); ????cout << (*sp1).v << endl;??????? // 10 同sp1->v相同 ????vector<shared_ptr<Foo>> vec; ????vec.push_back(sp1); ????cout << sp1.use_count() << endl; // 2 ????return 0; // vector先析構,里面存儲的對象引用計數減1,但不為0,不釋放對象 // sp1后析構,引用計數減1,變為0,釋放所指對象的內存資源 }
    主要方法:
    T& operator*() const; T* operator->() const; T* get() const; bool unique() const; long use_count() const; void swap(shared_ptr<T>& b);
    應用場景:

    ? ? 1. 在一個map(unordered_map)中,多key索引一個value,使用shared_ptr。

    #include <iostream> #include <memory> #include <map> #include <cstdint> using namespace std; Class SessionNode { public: ????SessionNode() {} ????virtual ~SessionNode() {} }; typedef std::shared_ptr<SessionNode> SessionNodeSP; int main(void) { ????map<uint64_t, SessionNodeSP> map; ????uint64_t imsi = 4600000; ????uint32_t tmsi = 0x12345; ????{ ????????SessionNodeSP sp(new SessionNode()); ????????map[imsi] = sp; ????????map[tmsi] = sp; ????????cout << sp.use_count() << endl;? // 3 ????} // sp 銷毀,引用計數減1,變為2 ????map.erase(tmsi);? // use_count()為1 ????map.erase(imsi);? // use_count()為0,釋放被管理對象的內存 ????return 0; }

    ? ? 2. 多個map索引同一value

    #include <iostream> #include <memory> #include <map> #include <cstdint> using namespace std; struct Node { ????uint64_t imsi; ????uint32_t tmsi; }; typedef std::shared_ptr<Node> NodeSP; class Session { public: ????Session() {} ????~Session() {} ????NodeSP GetNode(uint64_t imsi, uint32_t tmsi) { ????????NodeSP sp(new Node{imsi, tmsi}); ????????imsi_map_[imsi] = sp; ????????tmsi_map_[tmsi] = sp; ????????return sp; ????} ????void EraseNode(NodeSP& sp) { ????????if (sp == nullptr) ????????????return; ????????imsi_map_.erase(sp->imsi); ????????imsi_map_.erase(sp->tmsi); ????} private: ????map<uint64_t, NodeSP> imsi_map_; ????map<uint32_t, NodeSP> tmsi_map_; }; int main(void) { ????Session ses; ????uint64_t imsi = 4600000; ????uint32_t tmsi = 0x12345; ????NodeSP sp; ????sp = ses.GetNode(imsi, tmsi); ????cout << sp.use_count() << endl; // 3 ????// ... do something with sp ????ses.EraseNode(sp); ????cout << sp.use_count() << endl; // 1 ????sp.reset(); // 主動釋放被管理對象內存 ????return 0; }

    ? ? 3. 防止裸指針被刪除

    #include <iostream> #include <memory> class Cdr? { public: ????Cdr() {} protected: ????virtual ~Cdr() {} }; class SignalCdr : public Cdr { public: ????SignalCdr() {} ????virtual ~SignalCdr() {} }; typedef std::shared_ptr<Cdr> CdrSP; CdrSP CreateSignalCdr() { ????CdrSP sp(new SignalCdr()); ????return sp; }; int main(void) { ????CdrSP sp_cdr = CreateSignalCdr(); ????SignalCdr* p_signal_cdr = reinterpret_cast<SignalCdr*>(sp_cdr.get()); ????// ... do something //? delete p_signal_cdr; // 執行錯誤,~Cdr()為protected ????return 0; }

    ? ? 4. 定制刪除器。

    #include <iostream> #include <memory> #include <errno.h> #include <stdio.h> #include <string.h> using namespace std; class FileCloser { public: ????void operator()(FILE* p) { ????????// ... do something ????????cout << "close file" << endl; ????????fclose(p); ????} }; int main(void) { ????try { ????????FILE* p_file = fopen("./test.cpp", "r"); ????????if (p_file == nullptr) ????????????throw errno; ????????shared_ptr<FILE> sp_file(p_file, FileCloser()); //????? shared_ptr<FILE> sp_file(p_file, &fclose);? //如果只需要調用一個單參數的函數,可以直接這么寫 ????????// ... do something ????} catch (int& err) { ????????cout << strerror(err) << endl; ????} ????return 0; }

    ? ? 5. 在引起循環引用的時候使用weak_ptr。

    #include <iostream> #include <memory> using namespace std; struct Husband; struct Wife; typedef std::shared_ptr<Husband> HusbandSP; typedef std::shared_ptr<Wife>??? WifeSP; struct Wife { ????~Wife() { cout<< "wife distroy" << endl; } ????HusbandSP sp_hb; }; struct Husband { ????~Husband() { cout<< "husband distroy" << endl; } ????WifeSP sp_wf; }; int main(void) { ????{ ????????HusbandSP husband(new Husband()); ????????WifeSP??? wife(new Wife()); ????????husband->sp_wf = wife; ????????wife->sp_hb = husband; ????} // husband 和 wife,相互引用,離開作用域時引用計數都為1,造成內存泄露 ????return 0; }

    weak_ptr

    概念:
    • weak_ptr是為了配合shared_ptr而引入的一種智能指針,它只能夠通過shared_ptr或者weak_ptr來構造。
    • weak_ptr是作為shared_ptr的”觀察者“,并不修改shared_ptr所管理對象的引用計數,當shared_ptr銷毀時,weak_ptr會被設置為空,所以使用weak_ptr比底層指針的好處在于能夠知道所指對象是否有效
    • weak_ptr不具有普通指針的行為,因為沒有重載operator*和->。所以當weak_ptr觀察的對象存在,并且需要修改其內容時,需要提升為shared_ptr來操作。
    基本用法:
    #include <iostream> #include <memory> using namespace std; struct Cdr{ ????int v; }; typedef std::shared_ptr<Cdr> CdrSP; typedef std::weak_ptr<Cdr>?? CdrWP; void UpdateCdr(CdrWP wp) { ????if (wp.expired() == false) {?? // 檢查被管理對象是否被刪除,true 刪除,false 沒被刪除;比use_count()==1要快 ????????CdrSP sp = wp.lock();????? // 提升為強引用 //????? CdrSP sp(wp);????????????? // 另一種wp提升為強引用方法 ????????if (sp != nullptr) {?????? // 若提升失敗,shared_ptr 為 nullptr,此例子不會失敗 ????????????sp->v *= 2; ????????????cout << sp.use_count() << endl; // 此時引用計數為2 ????????} ????} // sp刪除,引用計數減1 ????wp.reset(); // 顯示釋放所有權,或者等離開作用域會自動釋放 } int main(void) { ????CdrSP sp(new Cdr{10}); ????UpdateCdr(sp);? // 對sp進行操作 ????cout << sp->v << endl;? // 20 ????return 0; }
    主要方法:
    long use_count() const; bool expired() const; std::shared_ptr<T> lock() const;
    應用場景:
  • 如基本用法,通過weak_ptr觀察shared_ptr管理的對象,如果有效,提升為shared_ptr進行操作。
  • map中存儲弱引用
    #include <iostream> #include <memory> #include <map> using namespace std; struct Cdr { ????int v; }; typedef std::shared_ptr<Cdr> CdrSP; typedef std::weak_ptr<Cdr>?? CdrWP; class Cache { public: ????Cache() {} ????~Cache() {} ????void UpdateIndex(CdrSP& sp) { ????????if (sp != nullptr && sp->v > 0) ????????????cdr_map_[sp->v] = sp; ????} private: ????map<int, CdrWP> cdr_map_; }; int main(void) { ????Cache cache; ????CdrSP sp_cdr(new Cdr{1}); ????cache.UpdateIndex(sp_cdr); ????cout << sp_cdr.use_count() << endl; // 1 ????return 0; // sp_cdr銷毀,引用計數為1,釋放管理對象內存 // cache銷毀,因為map中存儲的為weak_ptr,weap_ptr為空,所以不會產生二次釋放 }
  • bad_weak_ptr異常捕獲

    當shared_ptr通過weak_ptr參數構造,而weak_ptr指向一個已經被刪除的對象時,會拋出std::bad_weak_ptr異常。

    #include <iostream> #include <memory> using namespace std; int main() { ????shared_ptr<int> sp1(new int(1)); ????weak_ptr<int> wp(sp1); ????p1.reset(); ????try { ????????shared_ptr<int> sp2(wp); ????} catch (const std::bad_weak_ptr& e) { ????????cout << e.what() << endl;??? // "std::bad_weak_ptr" ????} }

    從this創建shared_ptr

    有時候,需要從this獲得 shared_ptr ,即是說,你希望你的類被shared_ptr所管理,你需要把"自身"轉換為shared_ptr的方法。

    #include <iostream> #include <memory> using namespace std; class Foo; typedef std::shared_ptr<Foo> FooSP; void DoSomething(const FooSP& sp) { ????cout << sp.use_count() << endl; // 2 } class Foo : public std::enable_shared_from_this<Foo> { public: ????Foo() {} ????~Foo() {} ????void Do() { ????????DoSomething(shared_from_this()); ????} }; int main(void) { ????FooSP sp(new Foo()); ????cout << sp.use_count() << endl; // 1 ????sp->Do(); ????return 0; }

    總結:

    • unique_ptr/shared_ptr中,get()把底層指針暴露出來,為的是兼容老程序,一般不提倡使用,因為很難確保別的例程會對這個指針做什么,比如說delete/delete[]。
    • 作為shared_ptr和weak_pt作為參數傳遞時,使用引用傳遞以減小開銷。
    • weak_ptr 沒有重載operator==,所以不能比較。shared_ptr可以比較,比較的是里面底層指針。
    • auto_ptr、unique_ptr、shared_ptr,不能這樣用
      { ????int* p = new int(100); ????auto_ptr<int> ap1(p); ????auto_ptr<int> ap2(p); }

      當離開作用域,ap1和ap2都試圖刪除p,會造成double free。

    • 使用智能指針雖然不需要手動處理引用計數和調用delete來釋放資源。但要清楚什么時候資源會被釋放。
      例如:用容器來存儲shared_ptr,并且從容器中刪除的時候釋放資源,那么其他例程在使用shared_ptr時只是更新其資源;
      如果從容器中刪除shared_ptr時不釋放資源,那么應該被另外一個shared_ptr所共享;這個時候容器中存儲weak_ptr更合適。

    引用:

    英文網址:
    http://en.cppreference.com/w/cpp/memory

    中文網址(google 機器翻譯):
    http://zh.cppreference.com/w/cpp/memory

    標簽:

    轉載于:https://www.cnblogs.com/457220157-FTD/p/4129058.html

    總結

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

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