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

            大龍的博客

            常用鏈接

            統(tǒng)計(jì)

            最新評(píng)論

            C++中使用多繼承時(shí)對(duì)象指針類型轉(zhuǎn)換的問(wèn)題 (轉(zhuǎn))

                  看過(guò)關(guān)于C++的書(shū)籍,知道了如果類中有虛函數(shù),那么對(duì)象的前四個(gè)字節(jié)就是一個(gè)指向稱為虛函數(shù)表的指針。

                  使用了多繼承之后的情況是怎樣呢?我以前并沒(méi)有認(rèn)真考慮過(guò)這個(gè)問(wèn)題,只是想當(dāng)然地認(rèn)為多個(gè)基類的虛函數(shù)表會(huì)合成一張表。

                  但是后來(lái)看過(guò)一些關(guān)于C++內(nèi)存對(duì)象模型的文章之后我知道我錯(cuò)了。這些文章我大體看懂了,但是有些細(xì)節(jié)不很明白,于是決定自己寫(xiě)代碼來(lái)進(jìn)行實(shí)驗(yàn)。

                  我用VC7寫(xiě)了一個(gè)動(dòng)態(tài)庫(kù),在某些程度上模仿了COM:

            interf.h:

            #define IID_X 1
            #define IID_Y 2

            class IBase
            {
            public:
                virtual int __stdcall Query(int iid,void ** ppv)=0;
                virtual int __stdcall AddRef()=0;
                virtual int __stdcall Release()=0;
            };

            class IX:public IBase
            {
            public:
                virtual unsigned long __stdcall FuncX1()=0;
            };

            class IY:public IBase
            {
            public:
                virtual unsigned long __stdcall FuncY1()=0;
            };

            class XY:public IX,IY
            {
            public:
                XY();
            protected:
                int i;
                int j;
                int m_ref;
                virtual ~XY();
            public:
                virtual int __stdcall Query(int iid,void ** ppv);
                virtual int __stdcall AddRef();
                virtual int __stdcall Release();

                virtual unsigned long __stdcall FuncX1();
                virtual unsigned long __stdcall FuncY1();
            };

            extern "C"
            {
            int _declspec(dllexport) NewObject(IX ** ppv);
            }

             

            xxyy.cpp:

            #include "stdafx.h"

            #include "interf.h"

            int XY::Query(int iid,void ** ppv)
            {
                if(IID_X==iid)
                {
                    *ppv=(IX *)this;
                    ((IX*)(*ppv))->AddRef();
                }
                else if(IID_Y==iid)
                {
                    *ppv=(IY *)this;
                    ((IY*)(*ppv))->AddRef();
                }
                else
                {
                    *ppv=NULL;
                    return 0;
                }
                return 1;
            }

            int XY::AddRef()
            {
                return ++m_ref;
            }

            int XY::Release()
            {
                if(0==--m_ref)
                    delete this;
                return m_ref;
            }

            XY::XY()
            {
                m_ref=1;
            }

            XY::~XY(){}

            unsigned long XY::FuncX1()
            {
                return (unsigned long)this;
            }

            unsigned long XY::FuncY1()
            {
                return (unsigned long)this;
            }

             

            MultiExt.cpp:

            // MultiExt.cpp : 定義 DLL 應(yīng)用程序的入口點(diǎn)。
            //

            #include "stdafx.h"

            #include "interf.h"

            BOOL APIENTRY DllMain( HANDLE hModule,
            DWORD ul_reason_for_call,
            LPVOID lpReserved
            )
            {
                return TRUE;
            }

            int _declspec(dllexport) NewObject(IX ** ppv)
            {
                *ppv=(IX *)new XY;
                if(NULL==*ppv)
                    return 0;
                return 1;
            }

             

                  這是一個(gè)動(dòng)態(tài)庫(kù)。編譯好之后,又用PureBasic寫(xiě)了一段程序來(lái)調(diào)用這個(gè)動(dòng)態(tài)庫(kù):

            ProtoType ProtoNewObject(ppv)

            Interface IBase
                Query(iid,ppv)
                AddRef()
                Release()
            EndInterface

            Interface IX Extends IBase
                FuncX1()
            EndInterface

            Interface IY Extends IBase
                FuncY1()
            EndInterface

            OpenLibrary(0,"MultiExt.dll")

            NewObject.ProtoNewObject=GetFunction(0,"NewObject")
            x.IX
            y.IY
            NewObject(@x)
            x\Query(2,@y)
            Debug x
            Debug y

            y\Release()

            x\Release()

            CloseLibrary(0)

                  運(yùn)行這個(gè)程序,就會(huì)發(fā)現(xiàn)一些讓人驚訝的內(nèi)容,2個(gè)Debug輸出的結(jié)果分別為:4136880、4136884。原來(lái)x指針和y指針指向了不同的地方。更明確地說(shuō),是y比x靠后4字節(jié)。x是由動(dòng)態(tài)庫(kù)函數(shù)NewObject獲取的,y是由x調(diào)用Query得來(lái)的。Query做了什么呢?看看上面的xxyy.cpp,原來(lái)Query將對(duì)象的指針強(qiáng)制轉(zhuǎn)換成為(IY*)并通過(guò)參數(shù)輸出。我原以為對(duì)指針進(jìn)行類型轉(zhuǎn)換只是對(duì)指針?biāo)阈g(shù)產(chǎn)生一些影響,沒(méi)想到此時(shí)的類型轉(zhuǎn)換竟然改變了指針的值。那么為什么和x相比y偏移了4個(gè)字節(jié)呢?

                  看了一些文章知道了原來(lái)在多繼承的時(shí)候,對(duì)象中已經(jīng)不止一個(gè)虛函數(shù)表指針了。像上面的示例程序,XY的對(duì)象中包含兩個(gè)虛函數(shù)表指針,一個(gè)來(lái)自IX,一個(gè)來(lái)自IY。由于IX和IY均沒(méi)有數(shù)據(jù)成員,所以屬于IY的虛函數(shù)表指針就放在了屬于IX的虛函數(shù)表指針的后面,屬于IX的虛函數(shù)表指針?lè)旁诹藢?duì)象的最前面。基于這種原理,x和y的值相差4。

                  但是x與y的不同帶來(lái)了一個(gè)問(wèn)題,那就是y沒(méi)有指向?qū)ο蟮氖椎刂罚墒峭ㄟ^(guò)一個(gè)接口指針調(diào)用虛成員函數(shù)時(shí),首先通過(guò)查找虛函數(shù)表來(lái)確定函數(shù)指針,這一點(diǎn)沒(méi)有問(wèn)題,因?yàn)閥就指向 指向IY虛函數(shù)表的指針(有點(diǎn)拗口)。問(wèn)題是調(diào)用函數(shù)時(shí)系統(tǒng)會(huì)把接口指針作為this參數(shù)傳遞給函數(shù),然而y已經(jīng)不能代表對(duì)象的首地址,也就是說(shuō)傳遞給函數(shù)的this參數(shù)不是對(duì)象的首地址。我想了想,應(yīng)該是編譯器在函數(shù)里“偷偷摸摸”加了一句:“this-=4;”,大家會(huì)說(shuō)this是const,的確,不過(guò)那僅僅針對(duì)程序員,編譯器可以為所欲為。

                  當(dāng)然,這只是一個(gè)想法,要證明這個(gè)想法得進(jìn)行實(shí)驗(yàn),得到能夠說(shuō)明問(wèn)題的數(shù)據(jù),我在上面的PureBasic程序中加了一些語(yǔ)句,修改后的程序如下(如果對(duì)下文的內(nèi)容不很明白可以參看《再談PureBasic的Interface》):

             

            ProtoType ProtoNewObject(ppv)

            Interface IBase
                Query(iid,ppv)
                AddRef()
                Release()
            EndInterface

            Interface IX Extends IBase
                FuncX1()
            EndInterface

            Interface IY Extends IBase
                FuncY1()
            EndInterface

            ProtoType ProtoQuery(this,iid,ppv)
            ProtoType ProtoAddRef(this)
            ProtoType ProtoRelease(this)
            ProtoType ProtoFuncY1(this)

            Structure IYVTable
                Query.ProtoQuery
                AddRef.ProtoAddRef
                Release.ProtoRelease
                FuncY1.ProtoFuncY1
            EndStructure

            OpenLibrary(0,"MultiExt.dll")

            NewObject.ProtoNewObject=GetFunction(0,"NewObject")
            x.IX
            y.IY
            NewObject(@x)
            x\Query(2,@y)

            *object.LONG=y
            *iyvt.IYVTable=*object\l
            Debug *iyvt\FuncY1(11)

            y\Release()

            x\Release()

            CloseLibrary(0)

                  我們給FuncY1傳入11,Debug顯示結(jié)果7。這足夠證明在XY::FuncY1()中編譯器加入了“this-=4;”。當(dāng)然不一定總是減4,減幾得根據(jù)虛函數(shù)表指針出現(xiàn)在對(duì)象中的位置來(lái)定,例如,我們給class IX添加一個(gè)數(shù)據(jù)成員long x,再用上面的PureBasic程序?qū)嶒?yàn),傳入11,得到Debug輸出“3”,原來(lái)在屬于IX的虛函數(shù)表指針和屬于IY的虛函數(shù)表之間夾了一個(gè)long x,所以只有將this自減8才能指向?qū)ο蟮氖椎刂贰?

                  C++的編譯程序?qū)⑦@些復(fù)雜的東西都隱藏了起來(lái),所以我們寫(xiě)起程序來(lái)輕松了很多。

            posted on 2010-04-14 12:22 大龍 閱讀(1204) 評(píng)論(0)  編輯 收藏 引用


            只有注冊(cè)用戶登錄后才能發(fā)表評(píng)論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問(wèn)   Chat2DB   管理


            亚洲精品无码久久千人斩| 久久精品国产亚洲一区二区三区| 久久人人爽爽爽人久久久| 中文字幕久久精品| 99久久综合国产精品免费| 色诱久久av| 国产成人精品久久一区二区三区av | 久久综合五月丁香久久激情| 7777久久久国产精品消防器材| 久久亚洲中文字幕精品一区| 97久久久久人妻精品专区| 精品国产青草久久久久福利| 国产亚洲精久久久久久无码| 久久免费看黄a级毛片| 久久亚洲精品无码播放| 久久青青草原国产精品免费| 国产精品乱码久久久久久软件| 久久99国产一区二区三区| 久久久无码精品午夜| 日韩影院久久| 国产∨亚洲V天堂无码久久久| 久久久婷婷五月亚洲97号色| 久久精品国产91久久综合麻豆自制| 亚洲∧v久久久无码精品| 久久精品一区二区三区AV| 久久国产视屏| 久久精品国产99久久久香蕉| 一本综合久久国产二区| 日本人妻丰满熟妇久久久久久| 日韩va亚洲va欧美va久久| 成人a毛片久久免费播放| 亚洲中文字幕伊人久久无码| 精品伊人久久大线蕉色首页| 欧美黑人又粗又大久久久| 精品久久久无码21p发布| 久久久久无码精品国产app| 久久久久免费看成人影片| 久久精品国产亚洲AV高清热| 精品人妻伦九区久久AAA片69| 久久人人爽人人爽人人片AV东京热| 亚洲伊人久久成综合人影院 |