<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          C++中智能指針的原理、使用、實現(xiàn)

          共 18629字,需瀏覽 38分鐘

           ·

          2021-06-08 14:17


          鏈接 | https://www.cnblogs.com/wxquare/p/4759020.html

          1、智能指針的作用

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

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

          1、從較淺的層面看,智能指針是利用了一種叫做RAII(資源獲取即初始化)的技術(shù)對普通的指針進行封裝,這使得智能指針實質(zhì)是一個對象,行為表現(xiàn)的卻像一個指針。

          2、智能指針的作用是防止忘記調(diào)用delete釋放內(nèi)存和程序異常的進入catch塊忘記釋放內(nèi)存。另外指針的釋放時機也是非常有考究的,多次釋放同一個指針會造成程序崩潰,這些都可以通過智能指針來解決。

          3、智能指針還有一個作用是把值語義轉(zhuǎn)換成引用語義。C++和Java有一處最大的區(qū)別在于語義不同,在Java里面下列代碼:

          Animal a = new Animal();
          Animal b = a;

          你當(dāng)然知道,這里其實只生成了一個對象,a和b僅僅是把持對象的引用而已。但在C++中不是這樣,

          Animal a;
          Animal b = a;

          這里卻是就是生成了兩個對象。

          關(guān)于值語言參考這篇文章http://www.cnblogs.com/Solstice/archive/2011/08/16/2141515.html

          2、智能指針的使用

          智能指針在C++11版本之后提供,包含在頭文件中,shared_ptr、unique_ptr、weak_ptr

          2.1 shared_ptr的使用

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

          • 初始化。智能指針是個模板類,可以指定類型,傳入指針通過構(gòu)造函數(shù)初始化。也可以使用make_shared函數(shù)初始化。不能將指針直接賦值給一個智能指針,一個是類,一個是指針。例如std::shared_ptr p4 = new int(1);的寫法是錯誤的

          • 拷貝和賦值。拷貝使得對象的引用計數(shù)增加1,賦值使得原對象引用計數(shù)減1,當(dāng)計數(shù)為0時,自動釋放內(nèi)存。后來指向的對象引用計數(shù)加1,指向后來的對象

          • get函數(shù)獲取原始指針

          • 注意不要用一個原始指針初始化多個shared_ptr,否則會造成二次釋放同一內(nèi)存

          • 注意避免循環(huán)引用,shared_ptr的一個最大的陷阱是循環(huán)引用,循環(huán),循環(huán)引用會導(dǎo)致堆內(nèi)存無法正確釋放,導(dǎo)致內(nèi)存泄漏。循環(huán)引用在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); //copy
                  std::cout << ptra.use_count() << std::endl;

                  int b = 20;
                  int *pb = &a;
                  //std::shared_ptr<int> ptrb = pb;  //error
                  std::shared_ptr<int> ptrb = std::make_shared<int>(b);
                  ptra2 = ptrb; //assign
                  pb = ptrb.get(); //獲取原始指針

                  std::cout << ptra.use_count() << std::endl;
                  std::cout << ptrb.use_count() << std::endl;
              }
          }

          2.2 unique_ptr的使用

          unique_ptr“唯一”擁有其所指對象,同一時刻只能有一個unique_ptr指向給定對象(通過禁止拷貝語義、只有移動語義來實現(xiàn))。相比與原始指針unique_ptr用于其RAII的特性,使得在出現(xiàn)異常的情況下,動態(tài)資源能得到釋放。unique_ptr指針本身的生命周期:從unique_ptr指針創(chuàng)建時開始,直到離開作用域。離開作用域時,若其指向?qū)ο螅瑒t將其所指對象銷毀(默認(rèn)使用delete操作符,用戶可指定其他操作)。unique_ptr指針與其所指對象的關(guān)系:在智能指針生命周期內(nèi),可以改變智能指針?biāo)笇ο螅鐒?chuàng)建智能指針時通過構(gòu)造函數(shù)指定、通過reset方法重新指定、通過release方法釋放所有權(quán)、通過移動語義轉(zhuǎn)移所有權(quán)。

          #include <iostream>
          #include <memory>

          int main() {
              {
                  std::unique_ptr<int> uptr(new int(10));  //綁定動態(tài)對象
                  //std::unique_ptr<int> uptr2 = uptr;  //不能賦值
                  //std::unique_ptr<int> uptr2(uptr);  //不能拷貝
                  std::unique_ptr<int> uptr2 = std::move(uptr); //轉(zhuǎn)換所有權(quán)
                  uptr2.release(); //釋放所有權(quán)
              }
              //超過uptr的作用域,內(nèi)存釋放
          }

          2.3 weak_ptr的使用

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

          2.4 循環(huán)引用

          考慮一個簡單的對象建模——家長與子女:a Parent has a Child, a Child knowshis/her Parent。在Java 里邊很好寫,不用擔(dān)心內(nèi)存泄漏,也不用擔(dān)心空懸指針,只要正確初始化myChild 和myParent,那么Java 程序員就不用擔(dān)心出現(xiàn)訪問錯誤。一個handle 是否有效,只需要判斷其是否non null。

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

          在C++里邊就要為資源管理費一番腦筋。如果使用原始指針作為成員,Child和Parent由誰釋放?那么如何保證指針的有效性?如何防止出現(xiàn)空懸指針?這些問題是C++面向?qū)ο缶幊搪闊┑膯栴},現(xiàn)在可以借助smart pointer把對象語義(pointer)轉(zhuǎn)變?yōu)橹担╲alue)語義,shared_ptr輕松解決生命周期的問題,不必?fù)?dān)心空懸指針。但是這個模型存在循環(huán)引用的問題,注意其中一個指針應(yīng)該為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;
          }

          循環(huán)引用內(nèi)存泄露的問題

          #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// 2
                  std::cout << c.use_count() << std::endl// 2
              }
              std::cout << wpp.use_count() << std::endl;  // 1
              std::cout << wpc.use_count() << std::endl;  // 1
              return 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_ptr
                  if (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// 2
                  std::cout << c.use_count() << std::endl// 1
              }
              std::cout << wpp.use_count() << std::endl;  // 0
              std::cout << wpc.use_count() << std::endl;  // 0
              return 0;
          }

          3、智能指針的設(shè)計和實現(xiàn)

          下面是一個簡單智能指針的demo。智能指針類將一個計數(shù)器與類指向的對象相關(guān)聯(lián),引用計數(shù)跟蹤該類有多少個對象共享同一指針。每次創(chuàng)建類的新對象時,初始化指針并將引用計數(shù)置為1;當(dāng)對象作為另一對象的副本而創(chuàng)建時,拷貝構(gòu)造函數(shù)拷貝指針并增加與之相應(yīng)的引用計數(shù);對一個對象進行賦值時,賦值操作符減少左操作數(shù)所指對象的引用計數(shù)(如果引用計數(shù)為減至0,則刪除對象),并增加右操作數(shù)所指對象的引用計數(shù);調(diào)用析構(gòu)函數(shù)時,構(gòu)造函數(shù)減少引用計數(shù)(如果引用計數(shù)減至0,則刪除基礎(chǔ)對象)。智能指針就是模擬指針動作的類。所有的智能指針都會重載 -> 和 * 操作符。智能指針還有許多其他功能,比較有用的是自動銷毀。這主要是利用棧對象的有限作用域以及臨時對象(有限作用域?qū)崿F(xiàn))析構(gòu)函數(shù)釋放內(nèi)存。

           1 #include <iostream>
           2 #include <memory>
           3 
           4 template<typename T>
           5 class SmartPointer {
           6 private:
           7     T* _ptr;
           8     size_t* _count;
           9 public:
          10     SmartPointer(T* ptr = nullptr) :
          11             _ptr(ptr) {
          12         if (_ptr) {
          13             _count = new size_t(1);
          14         } else {
          15             _count = new size_t(0);
          16         }
          17     }
          18 
          19     SmartPointer(const SmartPointer& ptr) {
          20         if (this != &ptr) {
          21             this->_ptr = ptr._ptr;
          22             this->_count = ptr._count;
          23             (*this->_count)++;
          24         }
          25     }
          26 
          27     SmartPointer& operator=(const SmartPointer& ptr) {
          28         if (this->_ptr == ptr._ptr) {
          29             return *this;
          30         }
          31 
          32         if (this->_ptr) {
          33             (*this->_count)--;
          34             if (this->_count == 0) {
          35                 delete this->_ptr;
          36                 delete this->_count;
          37             }
          38         }
          39 
          40         this->_ptr = ptr._ptr;
          41         this->_count = ptr._count;
          42         (*this->_count)++;
          43         return *this;
          44     }
          45 
          46     T& operator*() {
          47         assert(this->_ptr == nullptr);
          48         return *(this->_ptr);
          49 
          50     }
          51 
          52     T* operator->() {
          53         assert(this->_ptr == nullptr);
          54         return this->_ptr;
          55     }
          56 
          57     ~SmartPointer() {
          58         (*this->_count)--;
          59         if (*this->_count == 0) {
          60             delete this->_ptr;
          61             delete this->_count;
          62         }
          63     }
          64 
          65     size_t use_count(){
          66         return *this->_count;
          67     }
          68 };
          69 
          70 int main() {
          71     {
          72         SmartPointer<int> sp(new int(10));
          73         SmartPointer<int> sp2(sp);
          74         SmartPointer<int> sp3(new int(20));
          75         sp2 = sp3;
          76         std::cout << sp.use_count() << std::endl;
          77         std::cout << sp3.use_count() << std::endl;
          78     }
          79     //delete operator
          80 }

          往期推薦




          趣味設(shè)計模式
          音視頻開發(fā)
          C++ 進階
          超硬核 Qt
          玩轉(zhuǎn) Linux
          GitHub 開源推薦
          程序人生


          關(guān)注公眾號「高效程序員」??一起優(yōu)秀!

          回復(fù)“1024”,送你一份程序員大禮包。
          瀏覽 39
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  蜜桃av秘 无码一区二区 | 国产三级片视频网站 | 影音先锋aV成人无码电影 | 久久久波多野结衣 | 亚洲无码播放 |