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

            大龍的博客

            常用鏈接

            統計

            最新評論

            C++中使用多繼承時對象指針類型轉換的問題 (轉)

                  看過關于C++的書籍,知道了如果類中有虛函數,那么對象的前四個字節就是一個指向稱為虛函數表的指針。

                  使用了多繼承之后的情況是怎樣呢?我以前并沒有認真考慮過這個問題,只是想當然地認為多個基類的虛函數表會合成一張表。

                  但是后來看過一些關于C++內存對象模型的文章之后我知道我錯了。這些文章我大體看懂了,但是有些細節不很明白,于是決定自己寫代碼來進行實驗。

                  我用VC7寫了一個動態庫,在某些程度上模仿了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 應用程序的入口點。
            //

            #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;
            }

             

                  這是一個動態庫。編譯好之后,又用PureBasic寫了一段程序來調用這個動態庫:

            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)

                  運行這個程序,就會發現一些讓人驚訝的內容,2個Debug輸出的結果分別為:4136880、4136884。原來x指針和y指針指向了不同的地方。更明確地說,是y比x靠后4字節。x是由動態庫函數NewObject獲取的,y是由x調用Query得來的。Query做了什么呢?看看上面的xxyy.cpp,原來Query將對象的指針強制轉換成為(IY*)并通過參數輸出。我原以為對指針進行類型轉換只是對指針算術產生一些影響,沒想到此時的類型轉換竟然改變了指針的值。那么為什么和x相比y偏移了4個字節呢?

                  看了一些文章知道了原來在多繼承的時候,對象中已經不止一個虛函數表指針了。像上面的示例程序,XY的對象中包含兩個虛函數表指針,一個來自IX,一個來自IY。由于IX和IY均沒有數據成員,所以屬于IY的虛函數表指針就放在了屬于IX的虛函數表指針的后面,屬于IX的虛函數表指針放在了對象的最前面。基于這種原理,x和y的值相差4。

                  但是x與y的不同帶來了一個問題,那就是y沒有指向對象的首地址,可是通過一個接口指針調用虛成員函數時,首先通過查找虛函數表來確定函數指針,這一點沒有問題,因為y就指向 指向IY虛函數表的指針(有點拗口)。問題是調用函數時系統會把接口指針作為this參數傳遞給函數,然而y已經不能代表對象的首地址,也就是說傳遞給函數的this參數不是對象的首地址。我想了想,應該是編譯器在函數里“偷偷摸摸”加了一句:“this-=4;”,大家會說this是const,的確,不過那僅僅針對程序員,編譯器可以為所欲為。

                  當然,這只是一個想法,要證明這個想法得進行實驗,得到能夠說明問題的數據,我在上面的PureBasic程序中加了一些語句,修改后的程序如下(如果對下文的內容不很明白可以參看《再談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顯示結果7。這足夠證明在XY::FuncY1()中編譯器加入了“this-=4;”。當然不一定總是減4,減幾得根據虛函數表指針出現在對象中的位置來定,例如,我們給class IX添加一個數據成員long x,再用上面的PureBasic程序實驗,傳入11,得到Debug輸出“3”,原來在屬于IX的虛函數表指針和屬于IY的虛函數表之間夾了一個long x,所以只有將this自減8才能指向對象的首地址。

                  C++的編譯程序將這些復雜的東西都隱藏了起來,所以我們寫起程序來輕松了很多。

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

            精品国产乱码久久久久软件| 久久久91人妻无码精品蜜桃HD| 久久久久亚洲精品日久生情| 无码任你躁久久久久久久| 一级女性全黄久久生活片免费| 香蕉久久久久久狠狠色| 性做久久久久久久| 久久国产精品无码网站| 99久久免费国产精品特黄| 国产精品福利一区二区久久| 久久乐国产精品亚洲综合| 蜜臀av性久久久久蜜臀aⅴ麻豆 | 精品久久8x国产免费观看| 狠狠精品干练久久久无码中文字幕 | 久久精品国产男包| 国产精自产拍久久久久久蜜| 久久久无码精品亚洲日韩京东传媒| 久久国产精品久久久| 久久精品国产99久久久古代| 伊人热人久久中文字幕| 男女久久久国产一区二区三区| 色老头网站久久网| 久久久久无码专区亚洲av| 日本精品久久久久中文字幕| 久久亚洲中文字幕精品有坂深雪 | 久久人人爽人人人人片av| 国产亚州精品女人久久久久久| av无码久久久久不卡免费网站| 亚洲综合伊人久久大杳蕉| 久久强奷乱码老熟女网站| 亚洲综合精品香蕉久久网97| 国内精品久久久人妻中文字幕| 久久久亚洲欧洲日产国码aⅴ| 久久久久亚洲国产| 久久久久亚洲精品日久生情 | 99久久精品国内| 国产一久久香蕉国产线看观看| 色欲综合久久躁天天躁蜜桃| 亚洲中文字幕久久精品无码喷水| 欧美国产成人久久精品| 伊人久久大香线蕉亚洲五月天|