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

歡迎訪問 生活随笔!

生活随笔

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

c/c++

c++11中智能指针的原理,使用,实现

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

文章目錄

  • 理解智能指針的原理
  • 智能指針的使用
    • shared_ptr的使用
    • unique_ptr的使用
    • weak_ptr的使用
    • 循環引用
  • 指針指針的設計和實現:

理解智能指針的原理

c++程序設計中使用堆內存是非常頻繁的操作,堆內存的申請和釋放都由程序員自己管理。程序員自己管理堆內存可以提高了程序的效率,但是整體來說堆內存的管理是非常麻煩的,c++11中引入了智能指針的概念,方便管理堆內存。使用普通指針,容易造成堆內存泄露(忘記釋放),二次釋放,程序發生異常時內存泄漏等問題等,使用智能指針能更好的管理堆內存。

理解智能指針需要從下面三個層次:

  • 從較淺的層面看,智能指針是利用了一種叫做RAII(資源獲取即初始化)的技術對普通的指針進行封裝,這使用智能指針實質是一個對象,行為表現的卻像個指針。
  • 智能指針的作用是防止忘記調用delete使用內存和程序異常的進入catch塊忘記釋放內存。另外指針的釋放時機也是非常有考究的,多次釋放同一個指針會造成內存崩潰,這些都可以通過智能指針來解決
  • 智能指針還有一個作用是把值語義轉換成引用語義。c++和java有一處最大的區別在于語義不同,在java里面有如下代碼:
  • Animal a=new Animal(); Animal b=a; 你當然知道,這里其實只生成一個對象,a和b僅僅是把持對象的引用而已,但c++中不是這樣, Animal a; Animal b=a; 這里卻是就是生成兩個對象

    智能指針的使用

    指針指針在c++11版本之后提供,包含在< memory >中,shared_ptr,unique_ptr,weak_ptr

    shared_ptr的使用

    shared_ptr多個指針指向相同的對象,shared_ptr使用引用計數,每一個shared_ptr的拷貝都指向相同的內存。每使用一次他,內部的引用計數加1,每析構一次,內部的引用計數減1,減為0時,自動刪除所指向的堆內存時。shared_ptr內部的引用計數是線程安全的,但是對象的讀取需要加鎖。

    • 初始化。智能指針是個模板類,可以指定類型,傳入指針通過構造函數 初始化,也可以使用make_shared 函數初始化,不能將指針賦值給一個智能指針,一個是類,一個是指針。例如std::shared_ptr< int >p4=new int (1);寫法是錯誤的
    • 拷貝和賦值。拷貝使得對象的引用計數增加1,賦值使得原對象引用計數減1,當計數為0時,自動釋放內存。后來指向的對象引用計數加1,指向后來的對象
    • get函數獲取原始指針
    • 注意不要用一個原始指針初始化多個shared_ptr,否則會造成二次釋放同一個內存
    • 注意避免循環引用,shared_ptr最大的陷阱就是循環引用,循環,循環引用會導致堆內存無法正確釋放,導致內存泄漏。循環引用在weak_ptr中介紹
    #include <iostream> #include <memory> int main() {{int a = 10;std::shared_ptr<int> ptra = std::make_shared<int>(a);std::shared_ptr<int> ptra2(ptra); //copystd::cout << ptra.use_count() << std::endl;int b = 20;int *pb = &a;//std::shared_ptr<int> ptrb = pb; //errorstd::shared_ptr<int> ptrb = std::make_shared<int>(b);ptra2 = ptrb; //assignpb = ptrb.get(); //獲取原始指針std::cout << ptra.use_count() << std::endl;std::cout << ptrb.use_count() << std::endl;} }

    unique_ptr的使用

    unique_ptr"唯一"擁有其所指對象,同一時刻智能有一個unique_ptr指向給定對象(通過禁止拷貝語義,只有移動語義來實現)。相比于原始指針unique_ptr用于其RAII的特性,使得在出現異常的情況下,動態資源能得到釋放。unique_ptr指針本身的生命周期:從unique_ptr指針創建時開始,直到離開作用域。離開作用域時,若其指向對象,則將其所指對象銷毀(默認使用delete操作符,用戶可指定其它操作)。unique_ptr指針與其所指對象的關系:在智能指針生命周期內,可以改變智能指針所指的對象,如創建智能指針時通過構造函數指定,通過reset方法重新指定,通過release方法釋放所有權,通過移動語義轉移所有權。

    #include <iostream> #include <memory> int main() {{std::unique_ptr<int> uptr(new int(10)); //綁定動態對象//std::unique_ptr<int> uptr2 = uptr; //不能賦值//std::unique_ptr<int> uptr2(uptr); //不能拷貝std::unique_ptr<int> uptr2 = std::move(uptr); //轉換所有權uptr2.release(); //釋放所有權}//超過uptr的作用域,內存釋放}

    weak_ptr的使用

    weak_ptr是為了配合shared_ptr而引入的一種只能指針,因為它不具有普通指針的行為,沒有重載operator * 和 ->,它的最大作用在于協助shared_ptr工作,像旁觀者那樣觀測資源的使用情況。weak_ptr可以從一個shared_ptr或者另一個weak_ptr對象構造,獲得資源的觀測權。但weak_ptr沒有共享資源,它的構造不會引起指針引用計數的增加。使用weak_ptr的成員函數use_count()可以觀測資源的引用計數,另一個成員函數的expired()的功能等價于use_count()==0,但更快,表示被觀測的資源(也就是shared_ptr的管理的資源)已經不復存在。weak_ptr可以使用一個非常重要的成員函數lock()從被觀測的shared_ptr獲得一個可用的shared_ptr對象,從而操作資源。但當expired()==true的時候,lock()函數將返回一個存儲空指針的shared_ptr。

    #include <iostream> #include <memory> int main() {{std::shared_ptr<int> sh_ptr = std::make_shared<int>(10);std::cout << sh_ptr.use_count() << std::endl;std::weak_ptr<int> wp(sh_ptr);std::cout << wp.use_count() << std::endl;if(!wp.expired()){std::shared_ptr<int> sh_ptr2 = wp.lock(); //get another shared_ptr*sh_ptr = 100;std::cout << wp.use_count() << std::endl;}}//delete memory }

    循環引用

    考慮一個簡單的對象建模——家長與子女,a Parent has a child ,a child knowshis/her parent。在java里面很好寫,不用擔心內存泄漏,也不用擔心空懸指針,只要正確初始化myChild和myParent,那么java程序員就不用擔心出現訪問錯誤。一個handle是否有效,只需要判斷其是否nonnull。

    public class Parent { private Child myChild; } public class Child { private Parent myParent; }

    在c++里邊就是為資源管理費一番腦筋,如果使用原始指針作為成員,Child和Parent由誰釋放?那么如何保證指針的有效性?如何防止出現空懸指針?這些問題是c++面向對象變成的麻煩的問題,現在可以借助smart pointer 把語義對象(pointer)轉變為值(value)語義,shared_ptr輕松解決聲明周期的問題,不用擔心空懸指針。但是這個模型存在循環引用的問題,注意其中一個指針應該為weak_ptr。

    原始指針的做法,容易出錯:

    #include <iostream> #include <memory> class Child; class Parent; class Parent { private:Child* myChild; public:void setChild(Child* ch) {this->myChild = ch;}void doSomething() {if (this->myChild) {}}~Parent() {delete myChild;} }; class Child { private:Parent* myParent; public:void setPartent(Parent* p) {this->myParent = p;}void doSomething() {if (this->myParent) {}}~Child() {delete myParent;} }; int main() {{Parent* p = new Parent;Child* c = new Child;p->setChild(c);c->setPartent(p);delete c; //only delete one}return 0; }

    循環引用內存泄漏的問題:

    #include <iostream> #include <memory> class Child; class Parent; class Parent { private:std::shared_ptr<Child> ChildPtr; public:void setChild(std::shared_ptr<Child> child) {this->ChildPtr = child;}void doSomething() {if (this->ChildPtr.use_count()) {}}~Parent() {} }; class Child { private:std::shared_ptr<Parent> ParentPtr; public:void setPartent(std::shared_ptr<Parent> parent) {this->ParentPtr = parent;}void doSomething() {if (this->ParentPtr.use_count()) {}}~Child() {} }; int main() {std::weak_ptr<Parent> wpp;std::weak_ptr<Child> wpc;{std::shared_ptr<Parent> p(new Parent);std::shared_ptr<Child> c(new Child);p->setChild(c);c->setPartent(p);wpp = p;wpc = c;std::cout << p.use_count() << std::endl; // 2std::cout << c.use_count() << std::endl; // 2}std::cout << wpp.use_count() << std::endl; // 1std::cout << wpc.use_count() << std::endl; // 1return 0; }

    正確的做法:

    #include <iostream> #include <memory> class Child; class Parent; class Parent { private://std::shared_ptr<Child> ChildPtr;std::weak_ptr<Child> ChildPtr; public:void setChild(std::shared_ptr<Child> child) {this->ChildPtr = child;}void doSomething() {//new shared_ptrif (this->ChildPtr.lock()) {}}~Parent() {} }; class Child { private:std::shared_ptr<Parent> ParentPtr; public:void setPartent(std::shared_ptr<Parent> parent) {this->ParentPtr = parent;}void doSomething() {if (this->ParentPtr.use_count()) {}}~Child() {} }; int main() {std::weak_ptr<Parent> wpp;std::weak_ptr<Child> wpc;{std::shared_ptr<Parent> p(new Parent);std::shared_ptr<Child> c(new Child);p->setChild(c);c->setPartent(p);wpp = p;wpc = c;std::cout << p.use_count() << std::endl; // 2std::cout << c.use_count() << std::endl; // 1}std::cout << wpp.use_count() << std::endl; // 0std::cout << wpc.use_count() << std::endl; // 0return 0; }

    指針指針的設計和實現:

    下面是一個簡單智能指針的demo。智能指針將一個累計數器和類指向的對象相關聯,引用計數跟蹤該類有多少個對象共享同一個指針。每次創建類的新對象時,初始化指針并將引用計數置位1;當對象作為另一個對象的副本而創建時,拷貝構造函數指針并增加與之相應的引用計數;每一個對象進行賦值時,賦值操作符減少左操作數所指對象的引用計數(如果引用計數為減至0,則刪除對象),并增加右操作數所指對象的引用計數;調用析構函數時,構造函數減少引用計數(如果引用計數減至0,則刪除基礎對象)。智能指針就是模擬指針動作的類。所有的智能指針都會重載->和*操作符,智能指針還有許多其它功能,比較有效的是自動銷毀。這主要是利用棧對象的有限作用域以及臨時對象(有限作用域實現)析構函數釋放內存

    #include <iostream>#include <memory>template<typename T>class SmartPointer {private:T* _ptr;size_t* _count;public:SmartPointer(T* ptr = nullptr) :_ptr(ptr) {if (_ptr) {_count = new size_t(1);} else {_count = new size_t(0);}}SmartPointer(const SmartPointer& ptr) {if (this != &ptr) {this->_ptr = ptr._ptr;this->_count = ptr._count;(*this->_count)++;}}SmartPointer& operator=(const SmartPointer& ptr) {if (this->_ptr == ptr._ptr) {return *this;}if (this->_ptr) {(*this->_count)--;if (this->_count == 0) {delete this->_ptr;delete this->_count;}}this->_ptr = ptr._ptr;this->_count = ptr._count;(*this->_count)++;return *this;}T& operator*() {assert(this->_ptr == nullptr);return *(this->_ptr);}T* operator->() {assert(this->_ptr == nullptr);return this->_ptr;}~SmartPointer() {(*this->_count)--;if (*this->_count == 0) {delete this->_ptr;delete this->_count;}} size_t use_count(){return *this->_count;}};int main() {{SmartPointer<int> sp(new int(10));SmartPointer<int> sp2(sp);SmartPointer<int> sp3(new int(20));sp2 = sp3;std::cout << sp.use_count() << std::endl;std::cout << sp3.use_count() << std::endl;}//delete operator}

    總結

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

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