• C++虚函数表分析


             在<<反C++>>文中提到的 VC6.0 上的编译器支持一个d1reportAllClassLayout的开关, 能够输出全部对象的内存布局信息, 我自己经常使用vs2005来开发,所以这里我就以vs2005为例讲怎么设置d1reportAllClassLayout。

            右键项目属性(Properties)—》配制属性(Configuration Properties)—》C/C++--》命令行(Command Line)的框里输入/d1reportAllClassLayout,就可以看类的对象布局。例如以下图:(当然也能够使用命令行:cl –d1reportSingleClassLayout[classname] test.cpp)


    測试代码:

    class CBase
    {
    public:
            virtual void fun(void) {}
    
    private:
            int m_valuable;
    };
    
    输出结果:

    class CBase size(8):
     +---
     0 | {vfptr}
     4 | m_valuable
     +---
    CBase::$vftable@:
     | &CBase_meta
     |  0
     0 | &CBase::fun
    CBase::fun this adjustor: 0
    
    CDerived:非virtual继承CBase,持有一个virtual重写方法和一个virtual新方法。一个成员变量
    class CDerived: public CBase
    {
    public:
    	void fun(void) { }
    	virtual void vfun(void) { }
    
    public:
    	int m_derived;
    };
    编译后输出结果:
    class CDerived size(12):
     +---
     | +--- (base class CBase)
     0 | | {vfptr}
     4 | | m_valuable
     | +---
     8 | m_derived
     +---
    CDerived::$vftable@:
     | &CDerived_meta
     |  0
     0 | &CDerived::fun
     1 | &CDerived::vfun
    CDerived::fun this adjustor: 0
    CDerived::vfun this adjustor: 0
    当中vftable@CBase的CBase::fun项在这里更新为CDerived::fun,同一时候添加了一项CDerived::vfun。
    CDerived2:virtual继承CBase,持有一个virtual重写方法和一个virtual新方法,一个成员变量。因此结构为vftable@自身,vbtable@自身,member@自身,以及CBase结构。
    总结:
    class CDerived2: virtual public CBase
    {
    public:
    	void fun(void) { }
    	virtual void vfun(void) { }
    
    public:
    	int m_derived;
    };
    结果输出:
    class CDerived2 size(20):
     +---
     0 | {vfptr}
     4 | {vbptr}
     8 | m_derived
     +---
     +--- (virtual base CBase)
    12 | {vfptr}
    16 | m_valuable
     +---
    CDerived2::$vftable@CDerived2@:
     | &CDerived2_meta
     |  0
     0 | &CDerived2::vfun
    CDerived2::$vbtable@:
     0 | -4
     1 | 8 (CDerived2d(CDerived2+4)CBase)
    CDerived2::$vftable@CBase@:
     | -12
     0 | &CDerived2::fun
    CDerived2::fun this adjustor: 12
    CDerived2::vfun this adjustor: 0
    vbi:    class  offset o.vbptr  o.vbte fVtorDisp
               CBase      12       4       4 0
    当中vftable@自身仅仅有一项:CDerived2::vfun(),vbtable@自身仅仅有一项:它virtual继承的父类CBase。而vftable@CBase原来的CBase::fun更新为CDerived2::fun。
    
    CDerived3:virtual继承CBase,因此结构为vbtable@自身,member@自身。CBase结构
    class CDerived3: virtual public CBase
    {
    public:
    	void fun(void) { }
    
    public:
    	int m_derived3;
    };
    结果输出:
    class CDerived3 size(16):
     +---
     0 | {vbptr}
     4 | m_derived3
     +---
     +--- (virtual base CBase)
     8 | {vfptr}
    12 | m_valuable
     +---
    CDerived3::$vbtable@:
     0 | 0
     1 | 8 (CDerived3d(CDerived3+0)CBase)
    CDerived3::$vftable@:
     | -8
     0 | &CDerived3::fun
    CDerived3::fun this adjustor: 8
    vbi:    class  offset o.vbptr  o.vbte fVtorDisp
               CBase       8       0       4 0
    CGDerived:继承CDerived2、CDerived3
    class CGDerived: public CDerived2, public CDerived3
    {
    public:
    	void vfun() { }
    	virtual void vgfun() { }
    
    public:
    	int m_gd;
    };
    输出:
    class CGDerived size(32):
     +---
     | +--- (base class CDerived2)
     0 | | {vfptr}
     4 | | {vbptr}
     8 | | m_derived
     | +---
     | +--- (base class CDerived3)
    12 | | {vbptr}
    16 | | m_derived3
     | +---
    20 | m_gd
     +---
     +--- (virtual base CBase)
    24 | {vfptr}
    28 | m_valuable
     +---
    CGDerived::$vftable@CDerived2@:
     | &CGDerived_meta
     |  0
     0 | &CGDerived::vfun
     1 | &CGDerived::vgfun
    CGDerived::$vbtable@CDerived2@:
     0 | -4
     1 | 20 (CGDerivedd(CDerived2+4)CBase)
    CGDerived::$vbtable@CDerived3@:
     0 | 0
     1 | 12 (CGDerivedd(CDerived3+0)CBase)
    CGDerived::$vftable@CBase@:
     | -24
     0 | &thunk: this-=12; goto CDerived2::fun
    CGDerived::vfun this adjustor: 0
    CGDerived::vgfun this adjustor: 0
    vbi:    class  offset o.vbptr  o.vbte fVtorDisp
               CBase      24       4       4 0
    因此首先是CDerived2的结构和CDerived3的结构,自己的新virtual方法vgfun则加入在最左父类CDerived2的虚函数表中。然后是自己的成员。

    最后。CDerived2和CDerived3的父类CBase结构也带入当中。

    它的fun默认指向CGDerived的最左父类CDerived2::fun。

    CGG:继承CGDerived class CGG: public CGDerived { public: int m_kc; }; 输出: class CGG size(36): +--- | +--- (base class CGDerived) | | +--- (base class CDerived2) 0 | | | {vfptr} 4 | | | {vbptr} 8 | | | m_derived | | +--- | | +--- (base class CDerived3) 12 | | | {vbptr} 16 | | | m_derived3 | | +--- 20 | | m_gd | +--- 24 | m_kc +--- +--- (virtual base CBase) 28 | {vfptr} 32 | m_valuable +--- CGG::$vftable@CDerived2@: | &CGG_meta | 0 0 | &CGDerived::vfun 1 | &CGDerived::vgfun CGG::$vbtable@CDerived2@: 0 | -4 1 | 24 (CGGd(CDerived2+4)CBase) CGG::$vbtable@CDerived3@: 0 | 0 1 | 16 (CGGd(CDerived3+0)CBase) CGG::$vftable@CBase@: | -28 0 | &thunk: this-=16; goto CDerived2::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase 28 4 4 0 添加一个变量,基本仅仅是把CGDerived的结构再套一层。最后加上自己的成员变量。

    vbtable所指向的父类结构依旧在最后。 空类 class CBase2 { }; 输出: class CBase2 size(1): +--- +--- CD2:virtual继承则一定会创建vbtable,用vbptr指针指向,因此size为4 class CD2: virtual public CBase2 { }; 输出: class CD2 size(4): +--- 0 | {vbptr} +--- +--- (virtual base CBase2) +--- CD2::$vbtable@: 0 | 0 1 | 4 (CD2d(CD2+0)CBase2) vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 4 0 4 0 CE:非virtual继承CD2和CDerived2。于是依照类的声明顺序,先带入CDerived2的结构,再带入CD2的结构(而不是按继承顺序) class CE: public CD2, public CDerived2 { }; 输出: class CE size(24): +--- | +--- (base class CDerived2) 0 | | {vfptr} 4 | | {vbptr} 8 | | m_derived | +--- | +--- (base class CD2) 12 | | {vbptr} | +--- +--- +--- (virtual base CBase2) +--- +--- (virtual base CBase) 16 | {vfptr} 20 | m_valuable +--- CE::$vftable@CDerived2@: | &CE_meta | 0 0 | &CDerived2::vfun CE::$vbtable@CD2@: 0 | 0 1 | 4 (CEd(CD2+0)CBase2) 2 | 4 (CEd(CE+12)CBase) CE::$vbtable@CDerived2@: 0 | -4 1 | 12 (CEd(CDerived2+4)CBase) CE::$vftable@CBase@: | -16 0 | &thunk: this-=4; goto CDerived2::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 16 12 4 0 CBase 16 12 8 0 CF:virtual继承CBase和CBase2,vbtable此时的项便有两个。此时依照继承的顺序,而不是依照类的声明顺序 class CF: virtual public CBase2, virtual public CBase { }; 输出: class CF size(12): +--- 0 | {vbptr} +--- +--- (virtual base CBase2) +--- +--- (virtual base CBase) 4 | {vfptr} 8 | m_valuable +--- CF::$vbtable@: 0 | 0 1 | 4 (CFd(CF+0)CBase2) 2 | 4 (CFd(CF+0)CBase) CF::$vftable@: | -4 0 | &CBase::fun vbi: class offset o.vbptr o.vbte fVtorDisp CBase2 4 0 4 0 CBase 4 0 8 0

    总结:

     继承方式:非virtual继承:导入各个父类的结构(依照父类声明的顺序,从上到下),自身member在最后

             重写virtual方法:更新该方法最早定义的类的vftable

            新的virtual方法:在最左父类的vftable添加

     继承方式:有virtual继承:在自身member后添加virtual父类的结构(依照子类继承的顺序从左到右)。同一时候在最前面添加vbtable(假设没有的话),添加一项指向父类结构

             重写virtual方法:更新该方法的最早定义的类的vftable

             新的virtual方法:在自身最前面添加vftable(假设没有的话),在自己的vftable添加


    附 全部源代码:

    #ifndef TEST_CBASE_H
    #define TEST_CBASE_H
    
    class CBase
    {
    public:
    	CBase();
    	explicit CBase(int valuabel);
    	~CBase();
    
    	virtual void fun(void) {}
    
    private:
    	int m_valuable;
    };
    
    class CDerived: public CBase
    {
    public:
    	void fun(void) { }
    	virtual void vfun(void) { }
    
    public:
    	int m_derived;
    };
    
    class CDerived2: virtual public CBase
    {
    public:
    	void fun(void) { }
    	virtual void vfun(void) { }
    
    public:
    	int m_derived;
    };
    
    class CDerived3: virtual public CBase
    {
    public:
    	void fun(void) { }
    
    public:
    	int m_derived3;
    };
    
    class CGDerived: public CDerived2, public CDerived3
    {
    public:
    	void vfun() { }
    	virtual void vgfun() { }
    
    public:
    	int m_gd;
    };
    
    class CGG: public CGDerived
    {
    public:
    	int m_kc;
    };
    
    
    class CBase2
    {
    
    };
    
    class CD2: virtual public CBase2
    {
    
    };
    
    class CE: public CD2, public CDerived2
    {
    
    };
    
    class CF: virtual public CBase2, virtual public CBase
    {
    
    };
    
    
    #endif//TEST_CBASE_H


    版权声明:本文博客原创文章,博客,未经同意,不得转载。

  • 相关阅读:
    Unity3d vector3.forward和transform.forward的区别!
    UGUI实现摇杆
    UGUI实现拼图游戏
    Unity3d打包发布Android程序
    兼容PC和手机触摸代码
    unity3d中Transform组件变量详解
    运算符重载
    扩展方法
    string.Format格式化输出
    网络编程之Socket的TCP协议实现客户端与客户端之间的通信
  • 原文地址:https://www.cnblogs.com/mfrbuaa/p/4675714.html
Copyright © 2020-2023  润新知