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

            Basic Sample Senario :

            Client 需要一種組件提供一種 FastString類(lèi), 此類(lèi)具有 int length() 方法

            解決方案:

            靜態(tài)鏈接 : 組件廠商將源代碼交給 client ,客戶(hù)將組件代碼與client 代碼編譯連接運(yùn)行。 如果組件代碼需要fix bug or update ,則client 端代碼需要重新編譯連接。 而且client軟件的不同實(shí)例使用各自的組件內(nèi)存對(duì)象。

            動(dòng)態(tài)鏈接 : 組件廠商使用DLL形式發(fā)放組件,此時(shí)不同的client實(shí)例可以共享組件在內(nèi)存中的代碼段。

            DLL的問(wèn)題:1.導(dǎo)出名稱(chēng)的問(wèn)題 : 不同的compiler可以使用不同的mangle name 用來(lái)區(qū)分 c++的函數(shù),那么使用不同的compiler的client和組件無(wú)法鏈接 (可以使用extern “C”解決全局函數(shù)名的問(wèn)題,使用 .DEF文件解決導(dǎo)出成員函數(shù)的問(wèn)題)

                               2.升級(jí)的問(wèn)題 :如果組件最初定義為

            class FastString
            {
                char* m_psz;
            public:
                FastString(const char* psz){strcpy(m_psz, psz);}
            };

                                                      而后廠商更改了組件的實(shí)現(xiàn)

            class FastString
            {
            int newmember;
            char* psz;
            public:

            FastString(const char* psz){strcpy(m_psz, psz);}

            }

            原來(lái)FastString對(duì)象大小為4字節(jié),現(xiàn)在變?yōu)?字節(jié),但是client端按照4字節(jié)分配對(duì)象, dll卻要向后面的4個(gè)字節(jié)存入一個(gè)指針,行為不可預(yù)料!

            解決這一問(wèn)題的一種方法便是每次發(fā)布便更改dll的名字,即1.0, 2.0, x.0 等等。但這樣比較弱啊!!

            這種問(wèn)題根本原因是啥呢?

            class 的關(guān)鍵在于封裝了其中的實(shí)現(xiàn)細(xì)節(jié),即用戶(hù)知道類(lèi)提供了哪些服務(wù)( public方法)就行了,不需要管類(lèi)的內(nèi)部到底使用了哪些成員變量。這樣一來(lái),只要接口沒(méi)變(類(lèi)提供功能),user就可以安心的使用任意版本的實(shí)現(xiàn)了。C++怎么不行呢?C++告訴用戶(hù)接口的同時(shí),也告訴了用戶(hù)類(lèi)的實(shí)現(xiàn)(對(duì)象布局)。比如類(lèi)對(duì)象有多大啊,每個(gè)成員的偏移啊(具體可以看Inside c++ object model)。知道了這些,客戶(hù)端使用接口的代碼就和DLL中的具體實(shí)現(xiàn)緊密的耦合起來(lái)了,杯具 啊~

            咋辦呢? 只要不讓client直接創(chuàng)建FastString就行了,這樣client的代碼就不會(huì)受到FastString實(shí)現(xiàn)變化的影響了。給FastString加一個(gè)Wrapper類(lèi),內(nèi)部嵌套一個(gè)FastString,所有對(duì)FastString的調(diào)用都foward給內(nèi)部的FastString member, 創(chuàng)建FastString 的任務(wù)在dll方面完成,client只知道Wrapper大小為4個(gè)字節(jié)--指向FastString的指針。這樣問(wèn)題解決了,但是太麻煩了,所有的接口都要包一層!! 而且多了一層調(diào)用!

            還有啥辦法么? 為了保證c++接口類(lèi)實(shí)現(xiàn)二進(jìn)制級(jí)別的兼容只能使用編譯器無(wú)關(guān)的特性:1.假設(shè)復(fù)合類(lèi)型表現(xiàn)形式相同(struct) 2. 傳參順序相同,可以使用指示符指定3.虛函數(shù)調(diào)用機(jī)制相同,即基于 vtbl 和 vptr. 基于這些假設(shè),我們創(chuàng)建的c++接口類(lèi)所有函數(shù)設(shè)置為虛函數(shù),那么不同compiler將為客戶(hù)端方法調(diào)用產(chǎn)生相同的機(jī)器代碼。定義了接口,便規(guī)定了所有繼承于他的類(lèi)的內(nèi)存結(jié)構(gòu)一定與它兼容。但此時(shí)不能告訴用戶(hù)類(lèi)的定義,否則重回上面的老路上了。怎么辦,只有接口客戶(hù)無(wú)法創(chuàng)建類(lèi)的定義,只有export一個(gè)創(chuàng)建類(lèi)對(duì)象的函數(shù)客戶(hù)了。  同上面的wrapper一樣,創(chuàng)建類(lèi)的操作僅僅在dll內(nèi)部調(diào)用,這意味著實(shí)際建造類(lèi)對(duì)象大小和布局的代碼與編譯實(shí)現(xiàn)類(lèi)的方法的代碼使用同樣的編譯器創(chuàng)建 (即ctor和call ctor的代碼由同一編譯器同時(shí)編譯)。由于虛析構(gòu)函數(shù)在vtbl的位置與compiler相關(guān),所以不能把它設(shè)置為虛函數(shù),只有顯示增加一個(gè)Delete函數(shù)完成析構(gòu)工作。

            OK,當(dāng)前我們得到的DLL中只有創(chuàng)建類(lèi)對(duì)象的函數(shù)需要用extern “C”export 給客戶(hù),其他的接口中的虛函數(shù)是通過(guò)虛表訪問(wèn)的,無(wú)需靠符號(hào)名字鏈接。

            進(jìn)一步的,如果我們要給接口增加一個(gè)功能呢? 如果直接在現(xiàn)有接口中方法聲明后加入新的方法,那么此方法會(huì)出現(xiàn)在vtbl的最后一欄,舊的client不會(huì)調(diào)用新方法,但是如果新的client訪問(wèn)老的對(duì)象呢? 不幸的事情發(fā)生了! 這樣做的問(wèn)題在于,修改公開(kāi)的接口就打破了對(duì)象的封裝性。

            那么增加接口功能只能通過(guò)設(shè)計(jì)一個(gè)接口繼承另一個(gè)接口,或者讓類(lèi)繼承多個(gè)接口來(lái)實(shí)現(xiàn)了。客戶(hù)可以在運(yùn)行時(shí)通過(guò)RTTI來(lái)詢(xún)問(wèn)對(duì)象,支持這個(gè)功能不,親?然而 ,RTTI也是一個(gè)compiler相關(guān)的東東,好吧,我們讓每個(gè)類(lèi)自己實(shí)現(xiàn)RTTI,也就是實(shí)現(xiàn)一個(gè)dynamic_cast 方法, 用來(lái)將自己cast成為自己實(shí)現(xiàn)的接口,如果不支持則返回 0 。

            例如:

            void* CFastString::Dynamic_Cast(const char* pszTypename)
            {
            void * pRev;
            if(strcmp(pszTypename, "IFastString") == 0)
            {
            pRev = static_cast<IFastString*>(this);
            }
            else if(strcmp(pszTypename , "IOut") == 0)
            {
            pRev = static_cast<IOut*>(this);
            }
            else if(strcmp(pszTypename , "IExtent") == 0)
            {
            pRev = static_cast<IFastString*>(this);
            }
            else
            {
            return 0;
            }

            return pRev;
            }

            注意cast到IExtent的時(shí)候用了IFastString,因?yàn)镮FastString 和 IOut都是從IExtent繼承的,寫(xiě)IExtent的話不知道用哪個(gè),用虛擬繼承可以使CFastString對(duì)象只有一份IExtent,為啥不用呢? 你懂得。。。跟前面答案一樣,編譯器相關(guān)。

            最后一個(gè)問(wèn)題是delete的問(wèn)題,用戶(hù)需要記得為每一個(gè)對(duì)象調(diào)用一次delete方法,而指針cast來(lái)cast去,想記得對(duì)象被delete沒(méi)有很難啊! 怎么辦? 用引用計(jì)數(shù)吧,把每個(gè)指針當(dāng)做具有生命周期的實(shí)體,創(chuàng)建時(shí)候計(jì)數(shù)++,銷(xiāo)毀時(shí)候--,等到0的時(shí)候就delete對(duì)象。

            大功告成,通過(guò)vptr和vtbl的二進(jìn)制防火墻,我們做到了可重用的二進(jìn)制組件,組件變化客戶(hù)無(wú)需重新編譯 。

            Feedback

            # re: COM 本質(zhì)論筆記 第一章 com 是一個(gè)更好的c++  回復(fù)  更多評(píng)論   

            2011-08-23 23:51 by 陳梓瀚(vczh)
            更新的時(shí)候連exe一起換掉就好了,現(xiàn)在網(wǎng)速那么快

            # re: COM 本質(zhì)論筆記 第一章 com 是一個(gè)更好的c++  回復(fù)  更多評(píng)論   

            2011-09-05 11:42 by 戰(zhàn)痘小天使
            up,期待第二篇
            国产精品女同久久久久电影院| 精品久久久久久无码人妻热| 精品欧美一区二区三区久久久 | 午夜精品久久久久久久| 久久久久国产精品熟女影院| 久久综合九色综合久99| 欧美喷潮久久久XXXXx| 国产—久久香蕉国产线看观看 | 久久久婷婷五月亚洲97号色| 亚洲国产精品久久久久久| 久久成人小视频| 丰满少妇高潮惨叫久久久| 久久久久综合中文字幕| 久久综合给合久久狠狠狠97色 | 久久久精品人妻一区二区三区四| 国产精品九九久久精品女同亚洲欧美日韩综合区| 精品久久久久久久中文字幕| 亚洲AV日韩精品久久久久久| 久久久久国产成人精品亚洲午夜| 日本精品久久久中文字幕| 久久久久亚洲AV无码永不| 久久无码人妻精品一区二区三区 | 欧美久久精品一级c片片| 97久久国产综合精品女不卡 | 久久精品水蜜桃av综合天堂| 国产精品乱码久久久久久软件| 国产午夜精品久久久久九九| 久久精品中文无码资源站| 伊人久久大香线焦AV综合影院| 久久精品成人免费国产片小草| 色综合久久综精品| www.久久热.com| 精品熟女少妇aⅴ免费久久| 久久99精品国产自在现线小黄鸭| 久久久久久伊人高潮影院| 国产美女亚洲精品久久久综合| 久久伊人中文无码| 中文字幕久久亚洲一区| 久久综合狠狠综合久久| 国产成人无码久久久精品一| 精品蜜臀久久久久99网站|