日韩黑丝制服一区视频播放|日韩欧美人妻丝袜视频在线观看|九九影院一级蜜桃|亚洲中文在线导航|青草草视频在线观看|婷婷五月色伊人网站|日本一区二区在线|国产AV一二三四区毛片|正在播放久草视频|亚洲色图精品一区

分享

C++智能指針auto

 torony 2016-01-02

       對于C/C++程序員來說, 內(nèi)存泄露是一個談之色變的話題, 很多時候, 機(jī)器運(yùn)行1天2天都是ok的, 但運(yùn)行到一個星期后, 就卡得要死。 實(shí)際上, 很多時候是內(nèi)存泄露造成的。 內(nèi)存泄露很容易引入, 但是定位起來非常非常難, 在內(nèi)存泄露初期, 通常沒有異常癥狀, 但隨著內(nèi)存泄露的累積, 內(nèi)存逐漸被啃光, 最終導(dǎo)致卡死或者死機(jī)。


       申請堆內(nèi)存, 又沒有正確釋放, 就會導(dǎo)致內(nèi)存泄露, 聽起來好可怕啊, 那有沒有什么辦法可以解決這一難題呢? 有的! 人類的智慧還是很厲害的。 我么知道, 棧對象在離開其作用域的時候, 會自動調(diào)用析構(gòu)函數(shù), 所以, 可以考慮把某一棧對象與某一堆內(nèi)存綁定,且在其析構(gòu)函數(shù)中釋放堆內(nèi)存,  那么, 在該棧對象離開作用域時, 堆內(nèi)存自動釋放, 這就是智能指針(本質(zhì)是棧對象)的原理,簡直是妙招啊。  這個棧對象裝得像指針一樣, 所以我們稱之為智能指針, 其實(shí), 它不過就是個普通的棧對象而已。 


        在本文中, 我們來介紹智能指針中的一種------auto_ptr,  并從源碼的角度來看看auto_ptr使用中存在的一些常見問題:


       我們先來看一段簡單的程序:

  1. #include <iostream>  
  2. #include <memory> // 有auto_ptr這個類模板  
  3. using namespace std;  
  4.   
  5. int main()  
  6. {  
  7.     auto_ptr<int> p(new int(100));  
  8.   
  9.     // p是個對象, 但重載了*運(yùn)算符, 所以我說它很能裝, 裝得像個指針  
  10.     cout << *p << endl; // 100  
  11.   
  12.     return 0;  
  13. }  


      C++是一門復(fù)雜的語言, 比這個更糟糕的是: 一些不合格的C++程序員正在用它。 我們看看上面的程序, 實(shí)際上, 編譯器做了太多的手腳, 這也是C++復(fù)雜的一個原因。 我們展開memory文件, 看看其中關(guān)于auto_ptr的代碼, 復(fù)制過來, 然后形成如下程序:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4.   
  5. // 微軟能把代碼寫成這樣, 也是夠風(fēng)騷的  
  6.   
  7.         // TEMPLATE CLASS auto_ptr  
  8. template<class _Ty>  
  9.     class auto_ptr {  
  10. public:  
  11.     typedef _Ty element_type;  
  12.     explicit auto_ptr(_Ty *_P = 0) _THROW0()  
  13.         : _Owns(_P != 0), _Ptr(_P) {}  
  14.     auto_ptr(const auto_ptr<_Ty>& _Y) _THROW0()  
  15.         : _Owns(_Y._Owns), _Ptr(_Y.release()) {}  
  16.     auto_ptr<_Ty>& operator=(const auto_ptr<_Ty>& _Y) _THROW0()  
  17.         {if (this != &_Y)  
  18.             {if (_Ptr != _Y.get())  
  19.                 {if (_Owns)  
  20.                     delete _Ptr;  
  21.                 _Owns = _Y._Owns; }  
  22.             else if (_Y._Owns)  
  23.                 _Owns = true;  
  24.             _Ptr = _Y.release(); }  
  25.         return (*this); }  
  26.     ~auto_ptr()  
  27.         {if (_Owns)  
  28.             delete _Ptr; }  
  29.     _Ty& operator*() const _THROW0()  
  30.         {return (*get()); }  
  31.     _Ty *operator->() const _THROW0()  
  32.         {return (get()); }  
  33.     _Ty *get() const _THROW0()  
  34.         {return (_Ptr); }  
  35.     _Ty *release() const _THROW0()  
  36.         {((auto_ptr<_Ty> *)this)->_Owns = false;  
  37.         return (_Ptr); }  
  38. private:  
  39.     bool _Owns;  
  40.     _Ty *_Ptr;  
  41.     };  
  42.   
  43.   
  44. int main()  
  45. {  
  46.     auto_ptr<int> p(new int(100));  
  47.   
  48.     // p是個對象, 但重載了*運(yùn)算符, 所以我說它很能裝, 裝得像個指針  
  49.     cout << *p << endl; // 100  
  50.   
  51.     return 0;  
  52. }  
       看了上面auto_ptr的源碼, 正在喝水的我, 差點(diǎn)嗆著了。 并想問: 微軟, 你還能再風(fēng)騷一點(diǎn)么疑問


       好吧, 我也懶得計(jì)較了。 于是, 對上面代碼進(jìn)行風(fēng)格整理, 并作出詳細(xì)的注釋, 就算是剖析一下auto_ptr的源碼吧:

  1. #include <iostream>  
  2. using namespace std;  
  3.   
  4.   
  5. // 簡單類  
  6. class A  
  7. {  
  8. public:  
  9.     void fun()  
  10.     {  
  11.       
  12.     }  
  13. };  
  14.   
  15.   
  16. template<class T>  
  17.   
  18. // 類模板  
  19. class auto_ptr   
  20. {  
  21. public:  
  22.   
  23.     // explicit構(gòu)造函數(shù), 禁止隱式轉(zhuǎn)化  
  24.     explicit auto_ptr(T *p = 0) throw()  
  25.         : m_bIsOwner(p != 0), m_ptr(p)   
  26.     {  
  27.         cout << "debug1" << endl;  
  28.     }  
  29.   
  30.     // owner轉(zhuǎn)移  
  31.     auto_ptr(const auto_ptr<T>& y) throw()  
  32.         : m_bIsOwner(y.m_bIsOwner), m_ptr(y.release())   
  33.     {  
  34.         cout << "debug2" << endl;  
  35.     }  
  36.   
  37.     // owner轉(zhuǎn)移  
  38.     auto_ptr<T>& operator=(const auto_ptr<T>& y) throw()  
  39.     {  
  40.         cout << "debug3" << endl;  
  41.   
  42.         if (this != &y) // 當(dāng)前對象不是y對象  
  43.         {  
  44.             cout << "debug4" << endl;  
  45.   
  46.             if (m_ptr != y.get()) // 當(dāng)前對象綁定的地址不是y對象綁定的地址  
  47.             {  
  48.                 cout << "debug5" << endl;  
  49.   
  50.                 if (m_bIsOwner) // 如果當(dāng)前對象已經(jīng)綁定堆, 則要先釋放  
  51.                 {  
  52.                     cout << "debug6" << endl;  
  53.                     delete m_ptr;  
  54.                 }  
  55.   
  56.                 cout << "debug7" << endl;  
  57.   
  58.                 m_bIsOwner = y.m_bIsOwner; // 轉(zhuǎn)移owner  
  59.             }  
  60.             else if (y.m_bIsOwner) // 當(dāng)前對象與y綁定到同一塊堆上, 且y是owner, 則把y的owner轉(zhuǎn)移給當(dāng)前對象  
  61.             {  
  62.                 cout << "debug8" << endl;  
  63.   
  64.                 m_bIsOwner = true;  
  65.             }  
  66.   
  67.             cout << "debug9" << endl;  
  68.   
  69.             m_ptr = y.release();  // 讓y不再是owner  
  70.         }  
  71.   
  72.         cout << "debug10" << endl;  
  73.   
  74.         return *this;  // 返回當(dāng)前對象的引用  
  75.     }  
  76.   
  77.     // 析構(gòu)函數(shù)  
  78.     ~auto_ptr()  
  79.     {  
  80.         cout << "debug11" << endl;    
  81.   
  82.         if (m_bIsOwner) // 只有擁有owner屬性才釋放堆, 這樣避免重復(fù)釋放  
  83.         {  
  84.             cout << "debug12" << endl;  
  85.   
  86.             delete m_ptr;  // 即使m_ptr是空指針也木有關(guān)系  
  87.         }  
  88.     }  
  89.   
  90.     // 重載對象的*運(yùn)算符, 使得對象"看起來"像指針, 可以執(zhí)行*p操作  
  91.     T& operator*() const throw()  
  92.     {  
  93.         cout << "debug13" << endl;  
  94.   
  95.         return *get();   
  96.     }  
  97.   
  98.     // 重載對象的->運(yùn)算符  
  99.     T *operator->() const throw()  
  100.     {  
  101.         cout << "debug14" << endl;  
  102.   
  103.         return get();   
  104.     }  
  105.   
  106.     // 獲得對象綁定的地址  
  107.     T *get() const throw()  
  108.     {  
  109.         cout << "debug15" << endl;  
  110.   
  111.         return m_ptr;   
  112.     }  
  113.   
  114.     // 去掉對象的owner屬性  
  115.     T *release() const throw()  
  116.     {  
  117.         cout << "debug16" << endl;  
  118.   
  119.         ((auto_ptr<T> *)this)->m_bIsOwner = false;  
  120.         return m_ptr;   
  121.     }  
  122.       
  123. private:  
  124.     bool m_bIsOwner;  // 對象是否擁有為owner的標(biāo)志  
  125.     T *m_ptr; // 對象綁定的指針  
  126. };  
  127.   
  128.   
  129. int main()  
  130. {  
  131.     {  
  132.         cout << "------------------------------" << endl;  
  133.   
  134.         // 用法錯誤, 因?yàn)闃?gòu)造函數(shù)中有explicit, 不允許類型轉(zhuǎn)化  
  135.         //auto_ptr<int> p = new int(10);  
  136.     }  
  137.   
  138.   
  139.     {  
  140.         cout << "------------------------------" << endl;  
  141.   
  142.         // ok  
  143.         auto_ptr<int> p(new int(10));  
  144.     }  
  145.   
  146.   
  147.     {     
  148.         cout << "------------------------------" << endl;  
  149.   
  150.         // 下面代碼有嚴(yán)重的運(yùn)行期錯誤, 實(shí)際上是嘗試delete棧上的內(nèi)容  
  151.         int a = 10;  
  152.         //auto_ptr<int> p(&a);  
  153.     }  
  154.   
  155.   
  156.     {     
  157.         cout << "------------------------------" << endl;  
  158.   
  159.         auto_ptr<int> p(new int(10));  
  160.   
  161.         // 錯誤, p雖然"看似像"指針, 其本質(zhì)是對象, delete p;是未定義行為  
  162.         //delete p;  
  163.     }  
  164.   
  165.   
  166.     {     
  167.         cout << "------------------------------" << endl;  
  168.   
  169.         int *q = new int(10);  
  170.         auto_ptr<int> p(q);  
  171.   
  172.         // 錯誤, q釋放一次, p釋放一次, 重復(fù)釋放啊  
  173.         //delete q;  
  174.     }  
  175.   
  176.   
  177.     {     
  178.         cout << "------------------------------" << endl;  
  179.   
  180.         auto_ptr<int> p0;  
  181.   
  182.         // 有debug3的打印, 但沒有debug4, 知道原因了吧  
  183.         p0 = p0;  
  184.     }  
  185.   
  186.   
  187.     {         
  188.         cout << "------------------------------" << endl;  
  189.   
  190.         auto_ptr<int> p0(new int(10));  
  191.   
  192.         // 注意, 這是初始化, 不是復(fù)制, 所以不會有debug3的打印  
  193.         auto_ptr<int> p1 = p0;   
  194.     }  
  195.   
  196.   
  197.     {         
  198.         cout << "------------------------------" << endl;  
  199.   
  200.         auto_ptr<int> p0(new int(10));  
  201.         auto_ptr<int> p1;   
  202.   
  203.         // 注意, 這才是賦值, 所有有debug3, debug4, debug5, debug7, debug9, debug10的打印  
  204.         // 為什么沒有debug6呢? 因?yàn)楫?dāng)前對象p1還不是owner  
  205.         p1 = p0;  
  206.     }  
  207.   
  208.   
  209.     {         
  210.         cout << "------------------------------" << endl;  
  211.   
  212.         auto_ptr<int> p0(new int(10));  
  213.         auto_ptr<int> p1(new int(20));  
  214.           
  215.         // 有debug6的打印, 因?yàn)楫?dāng)先釋放p1綁定的對象, 否則內(nèi)存又泄露了啊  
  216.         p1 = p0;  
  217.     }  
  218.   
  219.   
  220.     {         
  221.         cout << "------------------------------" << endl;  
  222.   
  223.         auto_ptr<int> p0(new int(10));  
  224.   
  225.         // 把owner轉(zhuǎn)給p1  
  226.         auto_ptr<int> p1(p0);  
  227.   
  228.         // 終于見到你了, debug8  
  229.         p0 = p1;  
  230.     }  
  231.   
  232.     {         
  233.         cout << "------------------------------" << endl;  
  234.   
  235.         auto_ptr<int> p(new int(10));  
  236.   
  237.         // 見到你了, debug13  
  238.         cout << *p << endl;  
  239.     }  
  240.   
  241.       
  242.     {         
  243.         cout << "------------------------------" << endl;  
  244.   
  245.         auto_ptr<A> p(new A());  
  246.   
  247.         // 終于見到你了, debug15  
  248.         p->fun();  
  249.     }  
  250.   
  251.   
  252.     {         
  253.         cout << "------------------------------" << endl;  
  254.   
  255.         auto_ptr<int> p0(new int(10));  
  256.         auto_ptr<int> p1(p0);  
  257.         auto_ptr<int> p2(p1);  
  258.   
  259.         // 實(shí)際上, p3才是最后的winner, 才是最后的owner, 所以釋放堆的重任在p3身上  
  260.         auto_ptr<int> p3(p2);  
  261.     }  
  262.   
  263.   
  264.     {         
  265.         cout << "------------------------------" << endl;  
  266.   
  267.         // oh, my god, 內(nèi)存泄露, 本來要delete [] q; 現(xiàn)在析構(gòu)函數(shù)只執(zhí)行delete q;  
  268.         int *q = new int[3];  
  269.         auto_ptr<int> p(q);  
  270.     }  
  271.   
  272.   
  273.     {         
  274.         cout << "------------------------------" << endl;  
  275.   
  276.         // oh, my god, 內(nèi)存泄露, 本來要delete [] q; 現(xiàn)在析構(gòu)函數(shù)只執(zhí)行delete q;  
  277.         int *q = new int[3];  
  278.         auto_ptr<int> p(q);  
  279.   
  280.         // 已經(jīng)說過, 下面語句會造成內(nèi)存重復(fù)釋放  
  281.         //delete q;  
  282.     }  
  283.   
  284.   
  285.     // 最后說明一下,  auto_ptr不適合做容器的元素, 這一點(diǎn)我們以后會再次討論到  
  286.   
  287.   
  288.     return 0;  
  289. }  
    
       好了, 不多說auto_ptr了, 一切盡在代碼中。




    本站是提供個人知識管理的網(wǎng)絡(luò)存儲空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點(diǎn)擊一鍵舉報。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評論

    發(fā)表

    請遵守用戶 評論公約

    類似文章 更多