• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            posts - 200, comments - 8, trackbacks - 0, articles - 0

            這幾天開始拜讀侯捷先生和孟巖先生的譯作《C++標準程序庫:自修教程與參考手冊》 。兩位先生確實譯功上乘,讀得很順。但是讀到P55頁關于auto_ptr_ref的討論,卻百思不得其解:為什么需要引入auto_ptr_ref這個輔助類呢?

             

            從書中描述來看,仿佛與拷貝構造函數 、右值 、類型轉換 有關。于是,結合auto_ptr的源代碼,google之、baidu之,找了一推資料,終于初步 搞清該問題。

             

            auto_ptr的擁有權

            C++常見的智能指針有std::auto_ptr、boost::shared_ptr、boost::scoped_ptr、boost::shared_array、boost::scoped_array等。auto_ptr只是其中一種而已。但是,為什么auto_ptr才有auto_ptr_ref ,而boost::shared_ptr卻沒有shared_ptr_ref呢?

             

            答案與auto_ptr的特性有關。auto_ptr強調對資源的擁有權 (ownership)。也就是說,auto_ptr是"它所指對象"的擁有者。而一個對象只能屬于一個擁有者,嚴禁一物二主,否則就是重婚罪,意料外的災難將隨之而來。

             

            為了保證auto_ptr的擁有權唯一,auto_ptr的拷貝構造函數和賦值操作符做了這樣一件事情:移除另一個auto_ptr的擁有權 。為了說明擁有權的轉移 ,請看下面的代碼示例:

            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4.   
            5. int main(int argc, char **argv){  
            6.     auto_ptr<int> ptr1(new int(1));  
            7.     auto_ptr<int> ptr2(ptr1); //ptr1的擁有權被轉移到ptr2  
            8.   
            9.     auto_ptr<int> ptr3(NULL);  
            10.     ptr3 = ptr2;                //ptr2的擁有權被轉移到ptr3  
            11.   
            12.     cout<<ptr1.get()<<endl;     //結果為0  
            13.     cout<<ptr2.get()<<endl;     //結果為0  
            14.     cout<<*ptr3<<endl;          //結果為1  


            auto_ptr的拷貝構造函數與賦值操作符  

            由于需要實現擁有權的轉移,auto_ptr的拷貝構造函數和賦值操作符,與一般類的做法不太相同。我們可以看看MinGW 5.1.6實現的auto_ptr源代碼:

            Cpp代碼  收藏代碼
            1.  /** 
            2.  *  @brief  An %auto_ptr can be constructed from another %auto_ptr. 
            3.  *  @param  a  Another %auto_ptr of the same type. 
            4.  * 
            5.  *  This object now @e owns the object previously owned by @a a, 
            6.  *  which has given up ownsership. 
            7.  */  
            8. auto_ptr(auto_ptr& __a) throw() : _M_ptr(__a.release()) {}  
            9.   
            10. /** 
            11.  *  @brief  %auto_ptr assignment operator. 
            12.  *  @param  a  Another %auto_ptr of the same type. 
            13.  * 
            14.  *  This object now @e owns the object previously owned by @a a, 
            15.  *  which has given up ownsership.  The object that this one @e 
            16.  *  used to own and track has been deleted. 
            17.  */  
            18. auto_ptr&  
            19. operator=(auto_ptr& __a) throw () {  
            20.     reset(__a.release());  
            21.     return *this;  
            22. }  

             

                可以看到,auto_ptr的拷貝構造函數、賦值操作符,它們的參數都是auto_ptr& ,而不是auto_ptr const & 。

             

                一般來說,類的拷貝構造函數和賦值操作符的參數都是const &。但是auto_ptr的做法也是合理的:確保擁有權能夠轉移 。

             

                如果auto_ptr的拷貝構造函數和賦值操作符的參數是auto_ptr const & ,那么實參的擁有權將不能轉移。因為轉移擁有權需要修改auto_ptr的成員變量,而實參確是一個const對象,不允許修改。

             

            右值與const &

            假設我們想寫出下面的代碼:

            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4.   
            5. int main(int argc, char **argv) {  
            6.     auto_ptr<int> ptr1(auto_ptr<int>(new int(1)));  //使用臨時對象進行拷貝構造  
            7.     auto_ptr<int> ptr2(NULL);  
            8.     ptr2 = (auto_ptr<int>(new int(2)));           //使用臨時對象進行賦值  
            9. }  

             

                假設沒有定義auto_ptr_ref類及相關的函數,那么這段代碼將不能通過編譯。主要的原因是,拷貝構造函數及賦值操作符的參數:auto_ptr<int>(new int(1))和 auto_ptr<int>(new int(2)) 都是臨時對象 。臨時對象屬于典型的右值 ,而非const &是不能指向右值的 (參見More Effective C++ ,Item 19)。auto_ptr的拷貝構造函數及賦值操作符的參數類型恰恰是auto_ptr&,明顯 非const &。

             

                同理,下面的兩段代碼,也不會通過編譯:

            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4. auto_ptr<int> f();  
            5. int main(int argc, char **argv) {  
            6.     auto_ptr<int> ptr3(f());  //使用臨時對象進行拷貝構造  
            7.     auto_ptr<int> ptr4(NULL);  
            8.     ptr4 = f();               //使用臨時對象進行賦值  
            9. }  
            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4. auto_ptr<int> f(){  
            5.     return auto_ptr<int>(new int(3));  //這里其實也使用臨時對象進行拷貝構造  
            6. }  
             

                普通類不會遇到這個問題,是因為他們的拷貝構造函數及賦值操作符(不管是用戶定義還是編譯器生成的版本),參數都是const &。

             

            auto_ptr_ref之目的

            傳說當年C++標準委員會的好多國家,因為這個問題都想把auto_ptr從標準庫中剔除。好在Bill Gibbons和Greg Colvin創造性地提出了auto_ptr_ref,解決了這一問題,世界清靜了。

             

            auto_ptr_ref之原理

                很顯然,下面的構造函數,是可以接收auto_ptr臨時對象的。

            Cpp代碼  收藏代碼
            1. auto_ptr(auto_ptr __a) throw() : _M_ptr(__a.release()) { }  

             

                但另一個問題也很顯然:上述構造函數不能通過編譯。如果能通過編譯,就會陷入循環調用。我們稍作修改:

            Cpp代碼  收藏代碼
            1. auto_ptr(auto_ptr_ref<element_type> __ref) throw()  //element_type就是auto_ptr的模板參數。  
            2.       : _M_ptr(__ref._M_ptr) { }   

             

                該版本的構造函數,可以接收auto_ptr_ref的臨時對象。如果auto_ptr可以隱式轉換到auto_ptr_ref,那么我們就能夠用auto_ptr臨時對象來調用該構造函數。這個隱式轉換不難實現:

            Cpp代碼  收藏代碼
            1. template<typename _Tp1>  
            2.         operator auto_ptr_ref<_Tp1>() throw()  
            3.         { return auto_ptr_ref<_Tp1>(this->release()); }  
             

                至此,我們可以寫出下面的代碼,并可以通過編譯:

            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4.   
            5. int main(int argc, char **argv) {  
            6.     auto_ptr<int> ptr1(auto_ptr<int>(new int(1)));  //調用auto_ptr_ref版本的構造函數  
            7. }  

             

               同理,如果我們再提供下面的函數:

            Cpp代碼  收藏代碼
            1. auto_ptr&  
            2.      operator=(auto_ptr_ref<element_type> __ref) throw()  
            3.      {  
            4. if (__ref._M_ptr != this->get())  
            5.   {  
            6.     delete _M_ptr;  
            7.     _M_ptr = __ref._M_ptr;  
            8.   }  
            9. return *this;  
            10.      }  

             

                那么,下面的代碼也可以通過編譯:

            Cpp代碼  收藏代碼
            1. #include <iostream>  
            2. #include <memory>  
            3. using namespace std;  
            4.   
            5. int main(int argc, char **argv) {  
            6.     auto_ptr<int> ptr2(NULL);  
            7.     ptr2 = (auto_ptr<int>(new int(2)));  //調用auto_ptr_ref版本的賦值操作符  
            8. }  

             

            auto_ptr_ref之本質

            本質上,auto_ptr_ref賦予了auto_ptr“引用”的語義,這一點可以從auto_ptr_ref的注釋看出:

            Cpp代碼  收藏代碼
            1. /** 
            2.    *  A wrapper class to provide auto_ptr with reference semantics. 
            3.    *  For example, an auto_ptr can be assigned (or constructed from) 
            4.    *  the result of a function which returns an auto_ptr by value. 
            5.    * 
            6.    *  All the auto_ptr_ref stuff should happen behind the scenes. 
            7.    */  
            8.   template<typename _Tp1>  
            9.     struct auto_ptr_ref  
            10.     {  
            11.       _Tp1* _M_ptr;  
            12.         
            13.       explicit  
            14.       auto_ptr_ref(_Tp1* __p): _M_ptr(__p) { }  
            15.     };  


            auto_ptr_ref之代碼

            這里列出auto_ptr_ref相關的函數,共參考:

            Cpp代碼  收藏代碼
            1. auto_ptr(auto_ptr_ref<element_type> __ref) throw()  
            2. : _M_ptr(__ref._M_ptr) {}  
            3.   
            4. auto_ptr&  
            5. operator=(auto_ptr_ref<element_type> __ref) throw () {  
            6.     if (__ref._M_ptr != this->get()) {  
            7.         delete _M_ptr;  
            8.         _M_ptr = __ref._M_ptr;  
            9.     }  
            10.     return *this;  
            11. }  
            12.   
            13. template<typename _Tp1>  
            14. operator auto_ptr_ref<_Tp1>() throw () {  
            15.     return auto_ptr_ref<_Tp1> (this->release());  
            16. }  
            17.   
            18. template<typename _Tp1>  
            19. operator auto_ptr<_Tp1>() throw () {  
            20.     return auto_ptr<_Tp1> (this->release());  
            21. }  

             參考資料

            auto_ptr之變遷

            auto_ptr實現之我見

            auto_ptr_ref的奇妙(上)

            auto_ptr_ref的奇妙(下)

            auto_ptr_ref 的目的是什么

            關于auto_ptr_ref的一點問題

            左值和右值

            自:http://www.iteye.com/topic/746062

            久久久久亚洲AV成人网人人软件| 国产精品美女久久久m| 久久国产精品一区| 久久影院久久香蕉国产线看观看| 亚洲国产成人精品久久久国产成人一区二区三区综| 久久精品中文字幕一区| 亚洲AV无码1区2区久久| 亚洲国产成人久久精品影视| 久久中文字幕视频、最近更新| 97香蕉久久夜色精品国产| 国产精品天天影视久久综合网| 狠狠色丁香婷婷综合久久来来去 | 一本一本久久A久久综合精品| 久久精品国产亚洲av日韩| 久久久久久亚洲精品无码| 欧美黑人激情性久久| 久久se精品一区精品二区国产| 亚洲精品tv久久久久久久久久| 99久久精品费精品国产一区二区| 日本精品久久久久久久久免费| 精品精品国产自在久久高清| av色综合久久天堂av色综合在 | 久久青青草原亚洲av无码| 亚洲国产天堂久久久久久| 久久国产色av免费看| 国产99久久九九精品无码| 狠狠色噜噜狠狠狠狠狠色综合久久| 久久久久久亚洲AV无码专区| 少妇久久久久久被弄到高潮 | 久久综合给合久久狠狠狠97色| 国产视频久久| 亚洲精品高清久久| 久久久精品人妻一区二区三区蜜桃 | 国产激情久久久久影院老熟女| 久久久女人与动物群交毛片 | 精品国产乱码久久久久久1区2区| 久久久精品久久久久影院| 亚洲精品视频久久久| 一级a性色生活片久久无| 香蕉99久久国产综合精品宅男自| 久久久久国产一区二区三区|