• <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 - 319, comments - 22, trackbacks - 0, articles - 11
              C++博客 :: 首頁(yè) :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理

            DLL導(dǎo)出類(lèi)的問(wèn)題

            Posted on 2011-08-10 07:23 RTY 閱讀(837) 評(píng)論(0)  編輯 收藏 引用 所屬分類(lèi): C/C++Windows
            DLL導(dǎo)出類(lèi)的問(wèn)題

            http://www.diybl.com/course/3_program/c++/cppjs/200833/102641.html
            DLL動(dòng)態(tài)鏈接庫(kù)是程序復(fù)用的重要方式,DLL可以導(dǎo)出函數(shù),使函數(shù)被多個(gè)程序復(fù)用,DLL中的函數(shù)實(shí)現(xiàn)可以被修改而無(wú)需重新編譯和連接使用該DLL的應(yīng)用程序。作為一名面向?qū)ο蟮某绦騿T,希望DLL可以導(dǎo)出類(lèi),以便在類(lèi)的層次上實(shí)現(xiàn)復(fù)用。所幸的是,DLL確實(shí)也可以導(dǎo)出類(lèi)。

            然而事實(shí)卻沒(méi)這么簡(jiǎn)單,導(dǎo)出類(lèi)的DLL在維護(hù)和修改時(shí)有很多地方必需很小心,增加成員變量、修改導(dǎo)出類(lèi)的基類(lèi)等操作都可能導(dǎo)致意想不到的后果,也許
            用戶更新了最新版本的DLL庫(kù)后,應(yīng)用程序就再也不能工作了。這就是著名的DLL Hell(DLL地獄)問(wèn)題。

            DLL地獄問(wèn)題是怎么產(chǎn)生的呢?看下面的例子,假設(shè)DLL有一個(gè)導(dǎo)出類(lèi)ClassD1:
            class ClassD
            {
                   public:
            int GetInt();
                          private:
            int m_i;
            };
            int ClassD::GetInt()
            {
                   return m_i;
            }

            應(yīng)用程序使用現(xiàn)在的代碼來(lái)使用這個(gè)類(lèi):
            ClassD d;
            printf(“%d”, d.GetInt());
             
            程序進(jìn)行正正常,沒(méi)有什么問(wèn)題。后來(lái)DLL需要升級(jí),對(duì)ClassD進(jìn)行了修改,增加了一個(gè)成員變量,如下:
            class ClassD // 修改后
            {
                   public:
            int GetInt();
                          private:
                                 int m_i2;
            int m_i;
            };

            把新的DLL編譯連接完成后,復(fù)制到應(yīng)用程序目錄,這個(gè)倒楣的應(yīng)用程序調(diào)用GetInt方法恐怕再也無(wú)法得正確的值了。事實(shí)上它還算幸運(yùn)的,如果GetInt的實(shí)現(xiàn)改成如下這樣,那么它馬上就要出錯(cuò)退出了。
            int ClassD::GetInt() // 修改后
            {
                   return m_i++;
            }

            這樣的事情,稱(chēng)它是個(gè)地獄(Hell)一點(diǎn)也不夸張。為什么會(huì)出錯(cuò)呢?我們要先從類(lèi)實(shí)例的創(chuàng)建開(kāi)始,看看使用一個(gè)類(lèi)的工作過(guò)程。

            首先,程序語(yǔ)句“ClassD d;”為這個(gè)類(lèi)申請(qǐng)一塊內(nèi)存。這塊內(nèi)存保存該類(lèi)的所有成員變量,以及虛函數(shù)表。內(nèi)存的大小由類(lèi)的聲明決定,在應(yīng)用程序編譯時(shí)就已經(jīng)確定。

            然后,當(dāng)調(diào)用“d.GetInt()”時(shí),把申請(qǐng)的這一塊內(nèi)存做為this指針傳給GetInt函數(shù),GetInt函數(shù)從this指向的位置開(kāi)始,加上m_i應(yīng)有的偏移量,計(jì)算m_i所在的內(nèi)存位置,并從該位置取數(shù)據(jù)返回。m_i相對(duì)this的偏移量是由m_i在類(lèi)中定義的位置決定的,定義在前的成員變量在內(nèi)存中也更靠前。這個(gè)偏移量在DLL編譯時(shí)確定。

            當(dāng)ClassD的定義改為修改后的狀態(tài)時(shí),有些東西變了。

            第一個(gè)變的是內(nèi)存的大小。因?yàn)樾薷暮蟮腃lassD多了一個(gè)成員變量,所以內(nèi)存也變大了。然而這一點(diǎn)應(yīng)用程序并不知道。

            第二個(gè)變的是m_i的偏移地址。因?yàn)樵趍_i之前定義了一個(gè)m_i2,m_i的實(shí)現(xiàn)偏移地址實(shí)際已經(jīng)靠后了。所以d.GetInt()訪問(wèn)的將是原來(lái)m_i后面的那個(gè)位置,而這個(gè)位置已經(jīng)超出原來(lái)那塊內(nèi)存的后部范圍了。

            很顯然,在更換了DLL后,應(yīng)用程序還按原來(lái)的大小申請(qǐng)了一塊內(nèi)存,而它調(diào)用的方法卻訪問(wèn)了比這塊內(nèi)存更大的區(qū)域,出錯(cuò)再在所難免。

            同樣的情形還會(huì)發(fā)生在以下這些種情況中:

            1) 應(yīng)用程序直接訪問(wèn)類(lèi)的公有變量,而該公有變量在新DLL中定義的位置發(fā)生了變化;
            2) 應(yīng)用程序調(diào)用類(lèi)的一個(gè)虛函數(shù),而新的類(lèi)中,該虛函數(shù)的前面又增加了一個(gè)虛函數(shù);
            3) 新類(lèi)的后面增加了成員變量,并且新類(lèi)的成員函數(shù)將訪問(wèn)、修改這些變量;
            4) 修改了新類(lèi)的基類(lèi),基類(lèi)的大小發(fā)生了變化;

            等等,總言而之,一不小心,你的程序就會(huì)掉進(jìn)地獄。通過(guò)對(duì)這些引起出錯(cuò)的情況進(jìn)行分析,會(huì)發(fā)現(xiàn)其實(shí)只有三點(diǎn)變化會(huì)引起出錯(cuò),因?yàn)檫@三點(diǎn)是使用這個(gè)DLL的應(yīng)用程序在編譯時(shí)就需要確定的內(nèi)容,它們分別是:
            1) 類(lèi)的大小;
            2) 類(lèi)成員的偏移地址;
            3) 虛函數(shù)的順序。

            要想做一個(gè)可升級(jí)的DLL,必需避免以上三個(gè)問(wèn)題。所以以下三點(diǎn)用來(lái)使DLL遠(yuǎn)離地獄。

            1,不直接生成類(lèi)的實(shí)例。對(duì)于類(lèi)的大小,當(dāng)我們定義一個(gè)類(lèi)的實(shí)例,或使用new語(yǔ)句生成一個(gè)實(shí)例時(shí),內(nèi)存的大小是在編譯時(shí)決定的。要使應(yīng)用程序不依賴于類(lèi)的大小,只有一個(gè)辦法:應(yīng)用程序不生成類(lèi)的實(shí)例,使用DLL中的函數(shù)來(lái)生成。把導(dǎo)出類(lèi)的構(gòu)造函數(shù)定義為私有的(privated),在導(dǎo)出類(lèi)中提供靜態(tài)(static)成員函數(shù)(如NewInstance())用來(lái)生成類(lèi)的實(shí)例。因?yàn)镹ewInstance()函數(shù)在新的DLL中會(huì)被重新編譯,所以總能返回大小正確的實(shí)例內(nèi)存。

            2,不直接訪問(wèn)成員變量。應(yīng)用程序直接訪問(wèn)類(lèi)的成員變量時(shí)會(huì)用到該變量的偏移地址。所以避免偏移地址依賴的辦法就是不要直接訪問(wèn)成員變量。把所有的成員變量的訪問(wèn)控制都定義為保護(hù)型(protected)以上的級(jí)別,并為需要訪問(wèn)的成員變量定義Get或Set方法。Get或Set方法在編譯新DLL時(shí)會(huì)被重新編譯,所以總能訪問(wèn)到正確的變量位置。

            3,忘了虛函數(shù)吧,就算有也不要讓?xiě)?yīng)用程序直接訪問(wèn)它。因?yàn)轭?lèi)的構(gòu)造函數(shù)已經(jīng)是私有(privated)的了,所以應(yīng)用程序也不會(huì)去繼承這個(gè)類(lèi),也不會(huì)實(shí)現(xiàn)自己的多態(tài)。如果導(dǎo)出類(lèi)的父類(lèi)中有虛函數(shù),或設(shè)計(jì)需要(如類(lèi)工場(chǎng)之類(lèi)的框架),一定要把這些函數(shù)聲明為保護(hù)的(protected)以上的級(jí)別,并為應(yīng)用程序重新設(shè)計(jì)調(diào)用該慮函數(shù)的成員函數(shù)。這一點(diǎn)也類(lèi)似于對(duì)成員變量的處理。

            如果導(dǎo)出的類(lèi)能遵循以上三點(diǎn),那么以后對(duì)DLL的升級(jí)將可以認(rèn)為是安全的。

            如果對(duì)一個(gè)已經(jīng)存在的導(dǎo)出類(lèi)的DLL進(jìn)行維護(hù),同樣也要注意:不要改動(dòng)所有的成員變量,包括導(dǎo)出類(lèi)的父類(lèi),無(wú)論定義的順序還是數(shù)量;不要?jiǎng)铀械奶摵瘮?shù),無(wú)論順序還是數(shù)量。

            總結(jié)起來(lái),其實(shí)是一句話:導(dǎo)出類(lèi)的DLL不要導(dǎo)出除了函數(shù)以外的任何內(nèi)容。聽(tīng)起來(lái)是不是有點(diǎn)可笑呢!

            事實(shí)上,建議你在發(fā)布導(dǎo)出類(lèi)的DLL的時(shí)候,重新定義一個(gè)類(lèi)的聲明,這個(gè)聲明可以不管原來(lái)的類(lèi)里的成員變量之類(lèi)的,只把接口函數(shù)列在類(lèi)的聲明里,如下面的例子:
            class ClassInterface
            {
                   privated:
                          ClassInterface();
                   public:
                          static ClassInterface * NewInstance();
                          int GetXXX();
                          void SetXXX();
                          void Function();
            };

            使用該DLL的應(yīng)用程序用上面的定義作為ClassInterface的頭文件,便不會(huì)有任何可能導(dǎo)致的安全問(wèn)題。

            DLL地獄問(wèn)是歸根結(jié)底是因?yàn)镈LL當(dāng)初是作為函數(shù)級(jí)共享庫(kù)設(shè)計(jì)的,并不能真正提供一個(gè)類(lèi)所必需的信息。類(lèi)層上的程序復(fù)用只有Java和C#生成的類(lèi)文件才能做到。 

            天堂无码久久综合东京热| 浪潮AV色综合久久天堂| 99久久精品国产综合一区| 99久久精品免费看国产| 亚洲精品国产第一综合99久久| 伊人久久大香线蕉综合网站| 久久AV高清无码| 精品久久久久久久久久中文字幕 | 色欲久久久天天天综合网| 久久久亚洲欧洲日产国码二区| 亚洲国产精品久久久久婷婷老年| 久久久WWW成人免费毛片| 人妻丰满AV无码久久不卡| 国产精品综合久久第一页| 精品少妇人妻av无码久久| 久久这里的只有是精品23| 久久精品免费观看| 亚洲狠狠婷婷综合久久蜜芽| 久久强奷乱码老熟女| 国产99久久久久久免费看| 嫩草伊人久久精品少妇AV| 久久有码中文字幕| 久久免费视频6| 精品视频久久久久| 国产一区二区精品久久岳| 97久久精品无码一区二区| 色综合久久久久无码专区| 国产成人精品综合久久久久| 久久免费大片| 亚洲日本va午夜中文字幕久久| 国产成人无码精品久久久久免费| 久久99精品国产麻豆宅宅| WWW婷婷AV久久久影片| 国产高潮国产高潮久久久| 亚洲欧洲日产国码无码久久99| 久久免费看黄a级毛片| 少妇无套内谢久久久久| 中文国产成人精品久久不卡| 亚洲国产精品成人久久| 国产精品欧美久久久天天影视| 久久香蕉一级毛片|