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

            兔子的技術(shù)博客

            兔子

               :: 首頁 :: 聯(lián)系 :: 聚合  :: 管理
              202 Posts :: 0 Stories :: 43 Comments :: 0 Trackbacks

            留言簿(10)

            最新評論

            閱讀排行榜

            評論排行榜

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

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

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

            最簡單的一個(gè)資源泄漏的例子就是new和delete這樣的動(dòng)態(tài)內(nèi)存分配算子沒有正確使用造成的:

             

            struct A {

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

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

            };

            void area()

            {

                A 
            *= new A();

            }


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

             

            然而利用對象來管理資源是一個(gè)很好的辦法,因?yàn)閷ο蟮膶?shí)例本身在脫離作用域后會(huì)自動(dòng)清理,就像這樣

             

            class A_holder {

            public:

                expilict A_holder(A
            * p = NULL)

                :ptr(p) {}

             

                
            ~A_holder()

            {

                
            if (ptr)

                   delete ptr;

            }

            private:

                A
            * ptr;

            };

             

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

             

            void area()

            {

                A_holder ah(
            new A);

            }

             

            這樣,ah在離開area后會(huì)自動(dòng)調(diào)用其析構(gòu)函數(shù),就達(dá)到了自動(dòng)管理該資源的目的。

            利用C++的類的實(shí)例離開作用域會(huì)自動(dòng)調(diào)用其析構(gòu)函數(shù)的機(jī)制,可以比較方便的管理資源,但是在使用普通指針的情況下會(huì)存在多個(gè)指針指向同一對象的情況。

             

            void multi_point()

            {

                
            int a;

                
            int *p1,*p2;

             

                p1 
            = &a;

                p2 
            = &a;

            }

             

            實(shí)際的指針指向情況應(yīng)該是這樣

            p1 -à a ß- p2

             

            這里就出現(xiàn)了一個(gè)問題,我們想取消p1的時(shí)候可能會(huì)出現(xiàn)兩種語義:

            1、 將p1和其指向的對象一起刪除,這樣p2也就不可以繼續(xù)對a進(jìn)行使用。但是往往p2的使用者不會(huì)知道a已經(jīng)刪除,則出現(xiàn)了錯(cuò)誤。

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

             

            對于普通的delete操作,實(shí)現(xiàn)的是第一種情況,這樣通過p2對a進(jìn)行訪問必然會(huì)造成致命的錯(cuò)誤。

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

             

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

            一個(gè)簡單的Reference Count Smart Pointer的實(shí)現(xiàn)如下:

             



               #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*/

             

             

            由此,一個(gè)新的問題出現(xiàn)了!循環(huán)引用!

             

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

             



            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);

            }

             

             

            可以看見,一個(gè)對象A中有一個(gè)引用計(jì)數(shù)型智能指針,這樣的設(shè)計(jì)可能會(huì)很常見(指向自身類型的結(jié)構(gòu)體——鏈表)

            但是,當(dāng)自身循環(huán)引用發(fā)生的時(shí)候會(huì)怎么樣呢? 下面就來看看這么兩句代碼

             

             

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

             

             

            這里我們新建一個(gè)資源,并且把這個(gè)資源的管理權(quán)移交給pA這個(gè)引用計(jì)數(shù)型智能指針對象管理。如此,pA中的引用計(jì)數(shù)被初始化為1。

             

                pA->hold(pA);

             

            這里,我們把pA對象傳入給實(shí)例化的A對象中的引用計(jì)數(shù)型智能指針m_ptr,m_ptr執(zhí)行這樣的一個(gè)成員函數(shù):

             



                   
            //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;

                   }

             

            因?yàn)檫@里很明顯不是自身賦值,A中的m_ptr和pA不是同一個(gè)對象,所以進(jìn)入if結(jié)構(gòu)中調(diào)用下面的內(nèi)容。dispose是用作清理,因?yàn)閙_ptr并沒有指向任何東西,所以第一個(gè)函數(shù)并沒有真正的意義。

            然后

             

            m_ptr.ptr = pA.ptr;

            m_ptr.count 
            = pA.count;

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

             

            到此,pA的引用計(jì)數(shù)為2

             

            嗯,下面就pA這個(gè)對象理所當(dāng)然的離開了作用域,調(diào)用其析構(gòu)函數(shù):

             



                   
            ~CountedPtr () throw() {

                       dispose();

                   }

             
            噢,是一個(gè)轉(zhuǎn)向,調(diào)用其private成員函數(shù)dispose():

             

            void dispose() {

                       
            if (--*count == 0) {

                            delete count;

                            delete ptr;

                       }

                   }


            很簡單,將引用計(jì)數(shù)-1,由2變成1,不為0,所以if結(jié)構(gòu)內(nèi)的語句不被執(zhí)行。

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

             

            這樣的循環(huán)引用問題應(yīng)該是在設(shè)計(jì)的過程中就應(yīng)該避免的,如果用UML語言描述

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

             

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

            這樣A1,A2中對雙方的引用計(jì)數(shù)都是2,當(dāng)一方“銷毀”的時(shí)候,雙方的應(yīng)用計(jì)數(shù)都變?yōu)?,實(shí)際上并沒有銷毀任何東西,制造了兩個(gè)完美無暇的太空垃圾~

             

            這里又引發(fā)出一個(gè)問題,這樣的資源泄漏問題實(shí)際上還是由程序員自身引起的。

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

             

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

             

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

             

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/huntrose/archive/2008/11/18/3326388.aspx

            posted on 2009-09-04 11:22 會(huì)飛的兔子 閱讀(197) 評論(0)  編輯 收藏 引用 所屬分類: C++及開發(fā)環(huán)境
            女同久久| 91久久精品91久久性色| 国产精品免费久久| 思思久久99热免费精品6| 久久无码中文字幕东京热| 国产精品久久久福利| 模特私拍国产精品久久| 久久香综合精品久久伊人| 久久综合成人网| 久久国产乱子精品免费女| 久久精品人妻中文系列| 久久免费精品视频| 无码日韩人妻精品久久蜜桃 | 中文精品99久久国产 | 97久久精品人人澡人人爽| 久久精品a亚洲国产v高清不卡| 777久久精品一区二区三区无码 | 久久精品国产秦先生| 国产99久久精品一区二区| 狼狼综合久久久久综合网| 久久男人Av资源网站无码软件 | 东京热TOKYO综合久久精品| 一本一本久久A久久综合精品 | 久久永久免费人妻精品下载| 亚洲国产成人乱码精品女人久久久不卡 | 久久精品国产72国产精福利| 伊人久久大香线蕉亚洲五月天 | 国产麻豆精品久久一二三| 亚洲国产另类久久久精品小说| 久久无码一区二区三区少妇| 中文成人无码精品久久久不卡 | 日本精品久久久久影院日本| 99久久国产精品免费一区二区| 2021久久国自产拍精品| 久久久亚洲AV波多野结衣| 亚洲欧美精品伊人久久| 国产产无码乱码精品久久鸭| 久久国产精品免费一区| 久久最近最新中文字幕大全| 丁香狠狠色婷婷久久综合| 久久99国产精品一区二区|