• <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>

            兔子的技術博客

            兔子

               :: 首頁 :: 聯系 :: 聚合  :: 管理
              202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

            留言簿(10)

            最新評論

            閱讀排行榜

            評論排行榜

            原文:http://blog.csdn.net/huntrose/archive/2008/11/18/3326388.aspx

            指針 是C++中不得不談的一個話題,或許我還不是很能熟練的掌握指針以及我所要討論的引用計數型指針的全部,但是還是有那么些迫不及待想要表達一下。

            指針 pointer 是 資源泄漏 resource leak 的根源(當然可能還有其他一些什么東西,在我的映像中 異常 仿佛也會造成資源泄漏)

            最簡單的一個資源泄漏的例子就是new和delete這樣的動態內存分配算子沒有正確使用造成的:

             

            struct A {

                A()  { printf(
            "A Constructor!"); }

                
            ~A() { printf("A Destructor!"); }

            };

            void area()

            {

                A 
            *= new A();

            }


             執行完 area() 后,自然是只有A構造的消息,而A的析構卻不見影蹤。這里我們在離開了area作用域后,我們就無法對p所指向之資源進行操作,A的實例就會被懸掛在內存的某處得不到清理。一個形象點的比方就像人類發送的宇宙衛星失去了動力以及和地球的聯系,無法收回,就變成了宇宙垃圾~

             

            然而利用對象來管理資源是一個很好的辦法,因為對象的實例本身在脫離作用域后會自動清理,就像這樣

             

            class A_holder {

            public:

                expilict A_holder(A
            * p = NULL)

                :ptr(p) {}

             

                
            ~A_holder()

            {

                
            if (ptr)

                   delete ptr;

            }

            private:

                A
            * ptr;

            };

             

            如此,我們在area里面把資源的管理權力交給A_holder,就像下面這樣

             

            void area()

            {

                A_holder ah(
            new A);

            }

             

            這樣,ah在離開area后會自動調用其析構函數,就達到了自動管理該資源的目的。

            利用C++的類的實例離開作用域會自動調用其析構函數的機制,可以比較方便的管理資源,但是在使用普通指針的情況下會存在多個指針指向同一對象的情況。

             

            void multi_point()

            {

                
            int a;

                
            int *p1,*p2;

             

                p1 
            = &a;

                p2 
            = &a;

            }

             

            實際的指針指向情況應該是這樣

            p1 -à a ß- p2

             

            這里就出現了一個問題,我們想取消p1的時候可能會出現兩種語義:

            1、 將p1和其指向的對象一起刪除,這樣p2也就不可以繼續對a進行使用。但是往往p2的使用者不會知道a已經刪除,則出現了錯誤。

            2、 將p1與其指向的對象解除關系,這樣p2還可以對a進行使用。

             

            對于普通的delete操作,實現的是第一種情況,這樣通過p2對a進行訪問必然會造成致命的錯誤。

            在實現holder類的時候應該也考慮到第二種情況,如果有另外一個holder也指向這個資源,其中一個holder銷毀,另外一個holder還可能會使用到它們共同指向的那個資源。于是,holder的作用就不僅僅是單單的持有和施放資源,還應該處理有多少個對其hold資源的引用(即引用計數),并且在引用計數降為0時真正的銷毀資源實體。

             

            如此,一個行為類似指針(有->,*操作符)的智能指針出現,它管理賦予其資源的引用計數,也管理該資源的生死存亡。

            一個簡單的Reference Count Smart Pointer的實現如下:

             



               #ifndef COUNTED_PTR_HPP

               
            #define COUNTED_PTR_HPP

             

               
            /*class for counted reference semantics

                *-deletes the object to which it refers when the last CountedPtr

                * that refers to it is destroyed

                
            */

               template 
            <class T>

               
            class CountedPtr {

                 
            private:

                   T
            * ptr;        // pointer to the value

                   
            long* count;   // shared number of owners

             

                 
            public:

                   
            //initialize pointer with existing pointer

                   
            //-requires that the pointer p is a return value of new

                   
            explicit CountedPtr (T* p=0)

                    : ptr(p), count(
            new long(1)) {

                   }

             

                   
            //copy pointer (one more owner)

                   CountedPtr (
            const CountedPtr<T>& p) throw()

                    : ptr(p.ptr), count(p.count) {

                       
            ++*count;

                   }

             

                   
            //destructor (delete value if this was the last owner)

                   
            ~CountedPtr () throw() {

                       dispose();

                   }

             

                   
            //assignment (unshare old and share new value)

                   CountedPtr
            <T>& operator= (const CountedPtr<T>& p) throw() {

                       
            if (this != &p) {

                           dispose();

                           ptr 
            = p.ptr;

                           count 
            = p.count;

                           
            ++*count;

                       }

                       
            return *this;

                   }

             

                   
            //access the value to which the pointer refers

                   T
            & operator*() const throw() {

                       
            return *ptr;

                   }

                   T
            * operator->() const throw() {

                       
            return ptr;

                   }

             

                 
            private:

                   
            void dispose() {

                       
            if (--*count == 0) {

                            delete count;

                            delete ptr;

                       }

                   }

               };

             

               
            #endif /*COUNTED_PTR_HPP*/

             

             

            由此,一個新的問題出現了!循環引用!

             

            這樣的一個引用計數型智能指針目的是為了防止資源泄漏,但是只需要一個很小巧的代碼就可以讓這樣的初衷化為烏有……

             



            class A

            {

            public:

                A() {cout
            <<"A CON"<<endl;}

                
            ~A() {cout<<"A DES"<<endl;}

                

                
            void hold(CountedPtr<A> ptr)

                {

                   m_ptr 
            = ptr; 

                }

            private:

                CountedPtr
            <A> m_ptr;

            };

             

            void self_cir_area()

            {

                CountedPtr
            <A> pA(new A());

                pA
            ->hold(pA);

            }

             

             

            可以看見,一個對象A中有一個引用計數型智能指針,這樣的設計可能會很常見(指向自身類型的結構體——鏈表)

            但是,當自身循環引用發生的時候會怎么樣呢? 下面就來看看這么兩句代碼

             

             

                CountedPtr<A> pA(new A());

             

             

            這里我們新建一個資源,并且把這個資源的管理權移交給pA這個引用計數型智能指針對象管理。如此,pA中的引用計數被初始化為1。

             

                pA->hold(pA);

             

            這里,我們把pA對象傳入給實例化的A對象中的引用計數型智能指針m_ptr,m_ptr執行這樣的一個成員函數:

             



                   
            //assignment (unshare old and share new value)

                   CountedPtr
            <T>& operator= (const CountedPtr<T>& p) throw() {

                       
            if (this != &p) {

                           dispose();

                           ptr 
            = p.ptr;

                           count 
            = p.count;

                           
            ++*count;

                       }

                       
            return *this;

                   }

             

            因為這里很明顯不是自身賦值,A中的m_ptr和pA不是同一個對象,所以進入if結構中調用下面的內容。dispose是用作清理,因為m_ptr并沒有指向任何東西,所以第一個函數并沒有真正的意義。

            然后

             

            m_ptr.ptr = pA.ptr;

            m_ptr.count 
            = pA.count;

            ++(*m_ptr.count);  //(*pA.count)也被++

             

            到此,pA的引用計數為2

             

            嗯,下面就pA這個對象理所當然的離開了作用域,調用其析構函數:

             



                   
            ~CountedPtr () throw() {

                       dispose();

                   }

             
            噢,是一個轉向,調用其private成員函數dispose():

             

            void dispose() {

                       
            if (--*count == 0) {

                            delete count;

                            delete ptr;

                       }

                   }


            很簡單,將引用計數-1,由2變成1,不為0,所以if結構內的語句不被執行。

            由此,我們又制造了一個完美的太空垃圾……

             

            這樣的循環引用問題應該是在設計的過程中就應該避免的,如果用UML語言描述

            A中持有一個 引用計數型智能指針 的語義就是 這個 持有關系 是需要在 A消失的時候所持有的對象也隨之消失(這正是智能指針的作用,在脫離作用域自動清除其持有的資源)。如此就構成了 組合 關系。如果要表示 聚合 關系,即有 部分-整體 關系但是部分不隨整體的消失而消失,這就不是 智能指針 所表達的語義。

             

            還有可能遇見的循環引用就是 A1 持有 A2, A2 持有 A1 的情況……

            這樣A1,A2中對雙方的引用計數都是2,當一方“銷毀”的時候,雙方的應用計數都變為1,實際上并沒有銷毀任何東西,制造了兩個完美無暇的太空垃圾~

             

            這里又引發出一個問題,這樣的資源泄漏問題實際上還是由程序員自身引起的。

            C++之所以是一個很容易出錯的語言,很大一部分在于其資源的管理權力全權交給了程序員。這樣的權力到底是造福了程序員還是迷惑了程序員呢?

             

            這里我卻想起了蜘蛛俠中的一句名言: “一個人能力有多大,責任就有多大!”

             

            對C++中指針的指責不是一天兩天了,其易錯性無可厚非,但是它卻給了你其他語言無法給你的能力!這就是我的觀點,你能力有這么大,你就有責任來治理好這些資源。而非一再推卸責任。如果真的是要推卸責任,也就應該去選擇其他那些剝奪你的能力而減少你的責任的語言,因為你有選擇權!就像說英語和中文一樣,并沒有哪個人在強迫你,不是么?熱愛C++是一種態度,對一個語言的利弊都了然于心,了解其可以做什么不可以做什么,怎樣才可以更好的使用它來做什么,才能更好的使用它。更何況,there are rarely things that are not possible in C++

             

            本文來自CSDN博客,轉載請標明出處:http://blog.csdn.net/huntrose/archive/2008/11/18/3326388.aspx

            posted on 2009-09-04 11:22 會飛的兔子 閱讀(194) 評論(0)  編輯 收藏 引用 所屬分類: C++及開發環境
            久久久精品午夜免费不卡| 亚洲AV无一区二区三区久久 | 久久国产亚洲精品无码| 久久男人Av资源网站无码软件| 久久精品无码一区二区无码 | 国产精自产拍久久久久久蜜| 一级做a爰片久久毛片免费陪 | 国产午夜福利精品久久2021| 国产午夜福利精品久久| 久久精品国产亚洲AV香蕉| 久久精品国产91久久麻豆自制| 狠狠色丁香婷婷久久综合五月 | 久久精品夜色噜噜亚洲A∨| 久久久久久久97| 久久精品国产精品亚洲人人| 久久综合久久自在自线精品自| 国产三级观看久久| 国产99精品久久| 午夜欧美精品久久久久久久| 亚洲AⅤ优女AV综合久久久| 嫩草影院久久国产精品| 久久人人爽人人爽人人片AV高清 | 成人亚洲欧美久久久久| 久久亚洲精品成人AV| 人妻少妇精品久久| 99久久国产主播综合精品| 亚洲国产精品无码成人片久久| 午夜精品久久久久久影视777| 久久国产精品久久久| 久久这里只有精品18| 色欲久久久天天天综合网精品 | 久久精品一本到99热免费| 午夜视频久久久久一区 | 色综合久久无码中文字幕| 97视频久久久| 久久人人爽人人爽人人片AV高清| 久久久午夜精品福利内容| 亚洲精品午夜国产va久久| 欧美日韩精品久久久久 | 国产91久久综合| 91精品国产综合久久四虎久久无码一级 |