• <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>
            asm, c, c++ are my all
            -- Core In Computer
            posts - 139,  comments - 123,  trackbacks - 0

            [轉(zhuǎn)]智能指針與微妙的隱式轉(zhuǎn)換

            ??? C++
            雖然是強(qiáng)類型語言,但是卻還不如 Java C# 那么足夠的強(qiáng)類型,原因是允許的隱式轉(zhuǎn)換太多

            • C 語言繼承下來的基本類型之間的隱式轉(zhuǎn)換
            • T* 指針到 void* 的隱式轉(zhuǎn)換
            • non-explicit constructor 接受一個(gè)參數(shù)的隱式轉(zhuǎn)換
            • 從子類到基類的隱式轉(zhuǎn)換 ( 安全)
            • const non-const 的同類型的隱式轉(zhuǎn)換 ( 安全 )

            除開上面的五種隱式轉(zhuǎn)換外, C++ 的編譯器還非常聰明,當(dāng)沒法直接隱式轉(zhuǎn)換的時(shí)候,它會(huì)嘗試間接的方式隱式轉(zhuǎn)換,這使得有時(shí)候的隱式轉(zhuǎn)換非常的微妙,一個(gè)誤用會(huì)被編譯器接受而會(huì)出現(xiàn)意想不到的結(jié)果。例如假設(shè)類 A 有一個(gè) non-explicit constructor ,唯一的參數(shù)是類 B ,而類 B 也有一個(gè) non-explicit constructor 接受類型 C ,那么當(dāng)試圖用類型 C 的實(shí)例初始化類 A 的時(shí)候,編譯器發(fā)現(xiàn)沒有直接從類型 C 構(gòu)造的過程,但是呢,由于類 B 可以被接受,而類型 C 又可以向類型 B 隱式轉(zhuǎn)換,因此從 C->B->A 的路就通暢了。這樣的隱式轉(zhuǎn)換多數(shù)時(shí)候沒什么大礙,但是不是我們想要的,因?yàn)樗赡茉斐梢恍┪⒚畹?/span> bug 而難以捕捉。

            ?

            為了在培訓(xùn)的時(shí)候展示棧上析構(gòu)函數(shù)的特點(diǎn)和自動(dòng)資源管理,準(zhǔn)備下面的一個(gè)例子,結(jié)果測試的時(shí)候由于誤用而發(fā)現(xiàn)一些問題。 ( 測試的 IDE Visual Studio 2005)

            class A

            {

            public:

            A(){ a = 100; }

            int a;

            void f();

            };

            ?

            A * pa = new A();

            std::auto_ptr<A>? p = pa;? // 無意這樣使用的,本意是 std::auto_ptr<A> p(pa)

            p->f();

            ?

            這個(gè)寫法是拷貝構(gòu)造函數(shù)的形式,顯然從 T* 是不能直接拷貝構(gòu)造的 auto_ptr 的,但是編譯器會(huì)嘗試其他的路徑來轉(zhuǎn)換成 auto_ptr 來拷貝構(gòu)造,因此如果存在一個(gè)中間的 ,這個(gè)類能接受從 T* 的構(gòu)造,而 同時(shí)auto_ptr也能接受從類X 的構(gòu)造,那編譯器就會(huì)很高興的生成這樣的代碼。

            這段代碼在 VC6 上是通不過的,因?yàn)?/span> VC6 auto_ptr 實(shí)現(xiàn)就只有一個(gè)接受 T* 指針的 explicit constructor .

            但是 C++ Standard 的修正規(guī)范中,要求 auto_ptr 還應(yīng)該有個(gè)接受 auto_ptr_ref constructor 。那么這個(gè) auto_ptr_ref 是什么呢?按照 C++ Standard 的解釋 :

            Template auto_ptr_ref holds a reference to an auto_ptr. It is used by the auto_ptr conversions to allow auto_ptr objects to be passed to and returned from functions.

            有興趣可以參考 Scott Meyers " auto_ptr update page "? ( http://www.awprofessional.com/content/images/020163371X/autoptrupdate%5Cauto_ptr_update.html ?)講訴auto_ptr的歷史.

            ?

            再回到前面的代碼,本來應(yīng)該是通不過的編譯,但是 VC2005 的編譯器卻沒有任何怨言的通過 ( 即使把警告等級(jí)設(shè)置到 4) 。結(jié)果運(yùn)行的時(shí)候卻崩潰了,出錯(cuò)在 auto_ptr 的析構(gòu)函數(shù) ,delete 的指針?biāo)赶虻刂肥?/span> 100 ,而如果在 p->f() 后面加上一句 cout << pa->a << endl; 發(fā)現(xiàn)輸出結(jié)果為 0 為什么會(huì)這樣,原因就是前面所訴的間接的隱式轉(zhuǎn)換,這與 VC 2006 auto_ptr auto_ptr_ref 實(shí)現(xiàn)有關(guān),看看 P.J.Plauger 是怎么實(shí)現(xiàn)的 :

            // auto_ptr_ref

            template<class _Ty>

            struct auto_ptr_ref

            {

            // proxy reference for auto_ptr copying

            auto_ptr_ref(void *_Right)

            : _Ref(_Right)

            {?? // construct from generic pointer to auto_ptr ptr

            }

            void *_Ref;// generic pointer to auto_ptr ptr

            };

            ?

            // construct auto_ptr from an auto_ptr_ref object

            auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()

            {

            // construct by assuming pointer from _Right auto_ptr_ref

            _Ty **_Pptr = (_Ty **)_Right._Ref;

            _Ty *_Ptr = *_Pptr;

            *_Pptr = 0;

            // release old

            _Myptr = _Ptr;

            // reset this

            }

            ?

            這樣代碼通過編譯的原因也就清楚了, A* -> void * -> auto_ptr_ref -> auto_ptr -> copy constructor -> accept. 好長的隱式轉(zhuǎn)換鏈 , -_-, C++ 編譯器太聰明了。

            那么為什么最后會(huì)出現(xiàn)指針被破壞的結(jié)果呢,原因在 auto_ptr 的實(shí)現(xiàn),因?yàn)榘凑?/span> C++ Standard 要求, auto_ptr_ref 應(yīng)該是包含一個(gè) auto_ptr 的引用,因此 auto_ptr 的構(gòu)造函數(shù)也就假設(shè)了 auto_ptr_ref 的成員 _Ref 是一個(gè)指向 auto_ptr 的指針。 auto_ptr 中只有一個(gè)成員就是 A* 的指針,因此指向 auto_ptr 對象的指針相當(dāng)于就是個(gè) A** 指針,因此上面 auto_ptr auto_ptr_ref 構(gòu)造的代碼是合理的。 但是由于罪惡的 void* 造成了一條非常寬敞的隱式轉(zhuǎn)換的道路, A* 指針也能夠被接受,因此把 A* 當(dāng)作 A** 來使用,結(jié)果可想而知, A* 指向地址的前 4 個(gè)字節(jié) ( 因?yàn)?/span> 32 OS) 被拷貝出來,而這四個(gè)字節(jié)被賦值為 0( *_Pptr=0 ) 所以出現(xiàn)了最后的結(jié)果是 _Myptr 值為 100 ,而 pa->a 0

            如果要正確執(zhí)行結(jié)果,只要保證是個(gè) A** 指針就行了,有兩個(gè)方法

            第一, auto_ptr_ref 所包含的引用是指向的 auto_ptr 對象

            A * p = new A();

            std::auto_ptr<A> pt( new A() );

            std::auto_ptr_ref<A> ra( pt );

            std::auto_ptr<A> pb = ra ;

            pb->f();

            ?

            第二,直接用二級(jí)指針

            A * p = new A();

            std::auto_ptr<A> pb = &p;? // 這句話后 , p 將等于 0

            pb->f();

            ?

            當(dāng)然第二種是利用了 VC2005 的實(shí)現(xiàn)而造出來的,看著很別扭 ,:) 我不明白 P.J.Plauger 為什么用 void * ,而不是用 auto_ptr<T>& ,因?yàn)槿魏沃羔樁寄茈[式轉(zhuǎn)換為 void * ,這樣的危險(xiǎn)性大多了。并且如果用了 auto_ptr<T>& ,從 auto_ptr_ref 構(gòu)造也容易寫得更簡單清楚,看看以前的實(shí)現(xiàn)方式吧,仍然是 P.J.Plauger 的,但是版本低了點(diǎn):

            template<class _Ty>

            struct auto_ptr_ref

            {

            // proxy reference for auto_ptr copying

            ?

            auto_ptr_ref(auto_ptr<_Ty>& _Right)

            : _Ref(_Right)

            {

            // construct from compatible auto_ptr

            }

            auto_ptr<_Ty>& _Ref;

            // reference to constructor argument

            };

            auto_ptr(auto_ptr_ref<_Ty> _Right) _THROW0()

            : _Myptr(_Right._Ref.release())

            {

            // construct by assuming pointer from _Right auto_ptr_ref

            }

            ?

            這樣的實(shí)現(xiàn)方法,顯然不能接受任何指針的隱式轉(zhuǎn)換,也就防止一開始的那種錯(cuò)誤寫法,并且也是符合 C++ Standard 的要求的。

            SGI STL auto_ptr_ref 的實(shí)現(xiàn)則是包含了一個(gè) T* 的指針,構(gòu)造 auto_ptr 時(shí)候直接從 auto_ptr_ref 中拷貝這個(gè)指針,因此這樣的實(shí)現(xiàn)可以上代碼編譯通過,運(yùn)行也正確,不過不符合 C++ Standard

            ?

            總結(jié)一下,危險(xiǎn)的潛伏bug的隱式轉(zhuǎn)換應(yīng)該被杜絕的,特別是 void * 的隱式轉(zhuǎn)換和構(gòu)造函數(shù)的隱式轉(zhuǎn)換,因此建議是 :

            • 慎用 void * ,因?yàn)?/span> void * 必須要求你知道轉(zhuǎn)換前的實(shí)現(xiàn),因此更適合用在底層的、性能相關(guān)的內(nèi)部實(shí)現(xiàn)。
            • 單一參數(shù)的構(gòu)造函數(shù)應(yīng)該注意是否允許隱式轉(zhuǎn)換,如果不需要,加上 explicit 。例如 STL 容器中 vector 接受從 int 的構(gòu)造函數(shù),用于預(yù)先申請空間,這樣的構(gòu)造函數(shù)顯然不需要隱式轉(zhuǎn)換,因此加上了 explicit
            • 重載函數(shù)中,如果可能,就用更有明確意義的名字替代重載,因?yàn)殡[式轉(zhuǎn)換也許會(huì)帶來一些意想不到的麻煩。
            • 避免隱式轉(zhuǎn)換不等于是多用顯示轉(zhuǎn)換。 Meyers Effective C++ 中提到,即使 C++ 風(fēng)格的顯示轉(zhuǎn)換也應(yīng)該盡量少用,最好是改進(jìn)設(shè)計(jì)。
            posted on 2006-07-25 00:37 Jerry Cat 閱讀(780) 評論(0)  編輯 收藏 引用

            <2006年7月>
            2526272829301
            2345678
            9101112131415
            16171819202122
            23242526272829
            303112345

            常用鏈接

            留言簿(7)

            隨筆檔案

            最新隨筆

            搜索

            •  

            最新評論

            閱讀排行榜

            評論排行榜

            日本国产精品久久| 2021国产精品午夜久久| 亚洲?V乱码久久精品蜜桃 | 亚洲AV乱码久久精品蜜桃| 久久天天婷婷五月俺也去| 国内精品久久久久久久coent| 久久中文字幕一区二区| 国内精品久久久久久不卡影院| 久久亚洲高清综合| 伊人久久无码精品中文字幕| 久久精品男人影院| 久久综合久久综合久久| 精品久久久噜噜噜久久久| 久久久久久免费一区二区三区| 好久久免费视频高清| 久久精品国产只有精品66| 久久久久久国产精品美女| 欧美大香线蕉线伊人久久| 99久久国产主播综合精品| 99久久国产精品免费一区二区| 成人免费网站久久久| 国内精品久久久人妻中文字幕| 国产99久久久国产精免费| 久久99热国产这有精品| 欧美国产成人久久精品| 蜜臀久久99精品久久久久久| 热久久这里只有精品| AV狠狠色丁香婷婷综合久久| 亚洲国产成人精品女人久久久 | 久久久久人妻精品一区二区三区 | 久久久久亚洲av毛片大| 精品乱码久久久久久久| 99久久er这里只有精品18| 中文国产成人精品久久不卡| 久久婷婷人人澡人人爽人人爱| 成人精品一区二区久久| 国产成人精品综合久久久| 国产免费久久久久久无码| 久久精品国产欧美日韩| 99久久精品国内| 久久国产免费直播|