<menu id="w8yyk"><menu id="w8yyk"></menu></menu>
  • <dd id="w8yyk"><nav id="w8yyk"></nav></dd>
    <menu id="w8yyk"></menu>
    <menu id="w8yyk"><code id="w8yyk"></code></menu>
    <menu id="w8yyk"></menu>
    <xmp id="w8yyk">
    <xmp id="w8yyk"><nav id="w8yyk"></nav>
  • 網站首頁 > 物聯資訊 > 技術分享

    C++類對應的內存結構

    2016-09-28 00:00:00 廣州睿豐德信息科技有限公司 閱讀
    睿豐德科技 專注RFID識別技術和條碼識別技術與管理軟件的集成項目。質量追溯系統、MES系統、金蝶與條碼系統對接、用友與條碼系統對接

    提示1:對“內存結構”表示有疑問或不解的,先參考:

    http://blog.csdn.net/guogangj/archive/2007/05/25/1625199.aspx

    本文使用的表示方法和VC6的Memory視圖一致,即:左上表示低位。

     

    提示2:下文提到的“類大小”嚴格上來說是該類經過實例化的對象的大小。當然了,光研究長度的話,兩者差別不大,因為:CClassA objA,sizeof(CClassA)和sizeof(objA)得到的結果都是一樣的。

     

    一、真空類

    class CNull

    {

    };

    長度:1

    內存結構:

    ??

    評注:長度其實為0,這個字節作為內容沒有意義,可能每次都不一樣。

     

    二、空類

    class CNull2

    {

    public:

        CNull2(){printf("Construct/n");}

        ~CNull2(){printf("Desctruct/n");}

        void Foo(){printf("Foo/n");}

    };

    長度:1

    內存結構:

    ??

    評注:同真空類差不多,內部的成員函數并不會影響類大小。

     

    三、簡單類

    class COneMember

    {

    public:

        COneMember(int iValue = 0){m_iOne = iValue;};

    private:

        int m_iOne;

    };

    長度:4

    內存結構:

    00 00 00 00 //m_iOne

    評注:成員數據才影響類大小。

     

    四、簡單繼承

    class CTwoMember:public COneMember

    {

    private:

        int m_iTwo;

    };

    長度:8

    內存結構:

    00 00 00 00 //m_iOne

    CC CC CC CC //m_iTwo

    評注:子類成員接在父類成員之后。

     

    五、再繼承

    class CThreemember:public CTwoMember

    {

    public:

        CThreemember(int iValue=10) {m_iThree = iValue;};

    private:

        int m_iThree;

    };

    長度:12

    內存結構:

    00 00 00 00 //m_iOne

    CC CC CC CC //m_iTwo

    0A 00 00 00 //m_iThree

    評注:孫類成員接在子類之后,再再繼承就依此類推了。

     

    六、多重繼承

    class ClassA

    {

    public:

        ClassA(int iValue=1){m_iA = iValue;};

    private:

        int m_iA;

    };

     

    class ClassB

    {

    public:

        ClassB(int iValue=2){m_iB = iValue;};

    private:

        int m_iB;

    };

     

    class ClassC

    {

    public:

        ClassC(int iValue=3){m_iC = iValue;};

    private:

        int m_iC;

    };

     

    class CComplex :public ClassA, public ClassB, public ClassC

    {

    public:

        CComplex(int iValue=4){m_iComplex = iValue;};

    private:

        int m_iComplex;

    };

     

    長度:16

    內存結構:

    01 00 00 00  //A

    02 00 00 00  //B

    03 00 00 00  //C

    04 00 00 00  //Complex

    評注:也是父類成員先出現在前邊,我想這都足夠好理解。

     

    七、復雜一些的繼承

    不寫代碼了,怕讀者看了眼花,改畫圖。

    RFID設備管理軟件

    長度:32

    內存結構:

    01 00 00 00 //A

    02 00 00 00 //B

    03 00 00 00 //C

    04 00 00 00 //Complex

    00 00 00 00 //OneMember

    CC CC CC CC //TwoMember

    0A 00 00 00 //ThreeMember

    05 00 00 00 //VeryComplex

    評注:還是把自己的成員放在最后。

     

    只要沒涉及到“虛”(Virtual),我想沒什么難點,不巧的是“虛”正是我們要研究的內容。

     

    八、趁熱打鐵,看“虛繼承”

    class CTwoMember:virtual public COneMember

    {

    private:

        int m_iTwo;

    };

    長度:12

    內存結構:

    E8 2F 42 00 //指針,指向一個關于偏移量的數組,且稱之虛基類偏移量表指針

    CC CC CC CC // m_iTwo

    00 00 00 00 // m_iOne(虛基類數據成員)

    評注:virtual讓長度增加了4,其實是多了一個指針,關于這個指針,確實有些復雜,別的文章有具體分析,這里就不岔開具體講了,可認為它指向一個關于虛基類偏移量的數組,偏移量是關于虛基類數據成員的偏移量。

     

    九、“閉合”虛繼承,看看效果

    RFID設備管理軟件

    長度:24

    內存結構:

    14 30 42 00 //ClassB的虛基類偏移量表指針

    02 00 00 00 //m_iB

    C4 2F 42 00 //ClassC的虛基類偏移量表指針

    03 00 00 00 //m_iC

    04 00 00 00 //m_iComplex

    01 00 00 00 //m_iA

    評注:和預料中的一樣,虛基類的成員m_iA只出現了一次,而且是在最后邊。當然了,更復雜的情況要比這個難分析得多,但虛繼承不是我們研究的重點,我們只需要知道:虛繼承利用一個“虛基類偏移量表指針”來使得虛基類即使被重復繼承也只會出現一次。

     

    十、看一下關于static成員

    class CStaticNull

    {

    public:

        CStaticNull(){printf("Construct/n");}

        ~CStaticNull(){printf("Desctruct/n");}

        static void Foo(){printf("Foo/n");}

        static int m_iValue;

    };

    長度:1

    內存結構:(同CNull2)

    評注:可見static成員不會占用類的大小,static成員的存在區域為靜態區,可認為它們是“全局”的,只是不提供全局的訪問而已,這跟C的static其實沒什么區別。

     

    十一、帶一個虛函數的空類

    class CVirtualNull

    {

    public:

        CVirtualNull(){printf("Construct/n");}

        ~CVirtualNull(){printf("Desctruct/n");}

        virtual void Foo(){printf("Foo/n");}

    };

    長度:4

    內存結構:

    00 31 42 00 //指向虛函數表的指針(虛函數表后面簡稱“虛表”)

     

    00423100:(虛表)

    41 10 40 00 //指向虛函數Foo的指針

     

    00401041:

    E9 78 02 00 00 E9 C3 03 … //函數Foo的內容(看不懂)

    評注:帶虛函數的類長度就增加了4,這個4其實就是個指針,指向虛函數表的指針,上面這個例子中虛表只有一個函數指針,值就是“0x00401041”,指向的這個地址就是函數的入口了。

     

    十二、繼承帶虛函數的類

    class CVirtualDerived : public CVirtualNull

    {

    public:

        CVirtualDerived(){m_iVD=0xFF;};

        ~CVirtualDerived(){};

    private:

        int m_iVD;

    };

    長度:8

    內存結構:

    3C 50 42 00 //虛表指針

    FF 00 00 00 //m_iVD

     

    0042503C:(虛表)

    23 10 40 00 //指向虛函數Foo的指針,如果這時候創建一個CVirtualNull對象,會發現它的虛表的內容跟這個一樣

    評注:由于父類帶了虛函數,子類就算沒有顯式聲明虛函數,虛表還是存在的,虛表存放的位置跟父類不同,但內容是同的,也就是對父類虛表的復制。

     

    十三、子類有新的虛函數

    class CVirtualDerived: public CVirtualNull

    {

    public:

        CVirtualDerived(){m_iVD=0xFF;};

        ~CVirtualDerived(){};

        virtual void Foo2(){printf("Foo2/n");};

    private:

        int m_iVD;

    };

    長度:8

    內存結構:

    24 61 42 00 //虛表指針

    FF 00 00 00 //m_iVD

     

    00426124:(虛表)

    23 10 40 00

    50 10 40 00

    評注:虛表還是只有一張,不會因為增加了新的虛函數而多出另一張來,新的虛函數的指針將添加在復制了的虛表的后面。

     

    十四、當純虛函數(pure function)出現時

    class CPureVirtual

    {

        virtual void Foo() = 0;

    };

     

    class CDerivePV : public CPureVirtual

    {

        void Foo(){printf("vd: Foo/n");};

    };

    長度:4(CPureVirtual),4(CDerivePV)

    內存結構:

    CPureVirtual:

    (不可實例化)

     

    CDerivePV:

    28 50 42 00 //虛表指針

     

    00425028:(虛表)

    5A 10 40 00 //指向Foo的函數指針

    評注:帶純虛函數的類不可實例化,因此列不出其“內存結構”,由其派生類實現純虛函數。我們可以看到CDerivePV雖然沒有virtual聲明,但由于其父類帶virtual,所以還是繼承了虛表,如果CDerivePV有子類,還是這個道理。

     

    十五、虛函數類的多重繼承

    前面提到:(子類的虛表)不會因為增加了新的虛函數而多出另一張來,但如果有多重繼承的話情況就不是這樣了。下例中你將看到兩張虛表。

    RFID設備管理軟件

    大小:24

    內存結構

    F8 50 42 00 //虛表指針

    01 00 00 00 //m_iA

    02 00 00 00 //m_iB

    E8 50 42 00 //虛表指針

    03 00 00 00 //m_iC

    04 00 00 00 //m_iComplex

     

    004250F8:(虛表)

    5A 10 40 00 //FooA

    55 10 40 00 //FooB

    64 10 40 00 //FooComplex

     

    004250E8:(虛表)

    5F 10 40 00 //FooC

    評注:子類的虛函數接在第一個基類的虛函數表的后面,所以B接在A后面,Complex接在B后面。基類依次出現,子類成員接在最后面,所以m_iComplex位于最后面。

     

    本來還想看看更復雜些的情況,甚至包括虛繼承和虛函數同時出現的多重多層繼承情況,但確實有些復雜了,自己還有些找不到規律,所以準備之后再補充。

     

    from:http://blog.csdn.net/guogangj/article/details/2036785

    RFID管理系統集成商 RFID中間件 條碼系統中間層 物聯網軟件集成
    最近免费观看高清韩国日本大全