虚基类、虚函数,对象内存分布

虚基类、虚函数,对象内存分布


在Project Properties->C++->Command Line->Additional Options里面加上/d1reportSingleClassLayoutDerived
编译时,可输出类Derived的内存布局。



  非虚拟继承:
在派生类对象里,按照继承声明顺序依次分布基类对象,最后是派生类数据成员。
若基类声明了虚函数,则基类对象头部有一个虚函数表指针,然后是基类数据成员。
在基类虚函数表中,依次是基类的虚函数,若某个函数被派生类override,则替换为派生类的函数。
派生类独有的虚函数被加在第一个基类的虚函数表后面。

虚拟继承:
在派生类对象里,按照继承声明顺序依次分布非虚基类对象,然后是派生类数据成员,最后是虚基类对象。
若基类声明了虚函数,则基类对象头部有一个虚函数表指针,然后是基类数据成员。
在基类虚函数表中,依次是基类的虚函数,若某个函数被派生类override,则替换为派生类的函数。
若直接从虚基类派生的类没有非虚父类,且声明了新的虚函数,则该派生类有自己的虚函数表,在该派生类头部;否则派生类独有的虚函数被加在第一个非虚基类的虚函数表后面。
直接从虚基类派生的类内部还有一个虚基类表指针,在数据成员之前,非虚基类对象之后(若有的话)。
虚基类表中第一个值是该派生类起始地址到该表的偏移;之后的值依次是该派生类的虚基类到该表位置的地址偏移。


非虚拟单继承

class  Base
{
    
virtual void Func2(){cout<<"Base"<<endl;}
    
int iBase;
}
;

class  Derived :  public  Base
{
public:
    
void Func2(){cout<<"Derived Func2 called"<<endl;};
    
void Func3(){cout<<"Derived Func3 called"<<endl;};
    
virtual void Func5(){cout<<"Derived Func5 called"<<endl;};

    
int iDerived;
}
;


1>class Derived size(12):
1> +---
1> | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | iBase
1> | +---
1> 8 | iDerived
1> +---
1>Derived::$vftable@:
1> | &Derived_meta
1> |  0
1> 0 | &Derived::Func2
1> 1 | &Derived::Func5

非虚拟多继承

class  Base
{
    
virtual void Func2(){cout<<"Base"<<endl;}
    
int iBase;
}
;

class  Base1
{
public:
    
virtual void Func1(){cout<<"Base1 Func1 called"<<endl;};
    
virtual void Func2(){cout<<"Base1 Func2 called"<<endl;};
    
virtual void Func3(){cout<<"Base1 Func3 called"<<endl;};
    
int iBase1;
}
;

class  Base2
{
public:
    
virtual void Func2(){cout<<"Base2 Func2 called"<<endl;};
    
virtual void Func3(){cout<<"Base2 Func3 called"<<endl;};
    
virtual void Func4(){cout<<"Base2 Func4 called"<<endl;};
    
int iBase2;
}
;

class  Derived :  public  Base,  public  Base1,  public  Base2
{
public:
    
void Func2(){cout<<"Derived Func2 called"<<endl;};
    
void Func3(){cout<<"Derived Func3 called"<<endl;};
    
virtual void Func5(){cout<<"Derived Func5 called"<<endl;};
    
int iDerived;
}
;


1>class Derived size(28):
1> +---
1> | +--- (base class Base)
1> 0 | | {vfptr}
1> 4 | | iBase
1> | +---
1> | +--- (base class Base1)
1> 8 | | {vfptr}
1>12 | | iBase1
1> | +---
1> | +--- (base class Base2)
1>16 | | {vfptr}
1>20 | | iBase2
1> | +---
1>24 | iDerived
1> +---
1>Derived::$vftable@Base@:
1> | &Derived_meta
1> |  0
1> 0 | &Derived::Func2
1> 1 | &Derived::Func5
1>Derived::$vftable@Base1@:
1> | -8
1> 0 | &Base1::Func1
1> 1 | &thunk: this-=8; goto Derived::Func2
1> 2 | &Derived::Func3
1>Derived::$vftable@Base2@:
1> | -16
1> 0 | &thunk: this-=16; goto Derived::Func2
1> 1 | &thunk: this-=8; goto Derived::Func3
1> 2 | &Base2::Func4

虚拟单继承

class  Base
{
public:
    
virtual void Func2(){};
    
int BaseValue;
}
;

class  Derived :  virtual   public  Base
{
public:
    
void Func2(){};
    
virtual void Func4(){};
    
int DerivedValue;
}
;


1>class Derived size(20):
1> +---
1> 0 | {vfptr}
1> 4 | {vbptr}
1> 8 | DerivedValue
1> +---
1> +--- (virtual base Base)
1>12 | {vfptr}
1>16 | BaseValue
1> +---
1>Derived::$vftable@Derived@:
1> | &Derived_meta
1> |  0
1> 0 | &Derived::Func4
1>Derived::$vbtable@:
1> 0 | -4
1> 1 | 8 (Derivedd(Derived+4)Base)
1>Derived::$vftable@Base@:
1> | -12
1> 0 | &Derived::Func2

虚拟多继承

class  Base
{
    
virtual void Func2(){cout<<"Base"<<endl;}
    
int iBase;
}
;

class  Base1
{
public:
    
virtual void Func1(){cout<<"Base1 Func1 called"<<endl;};
    
virtual void Func2(){cout<<"Base1 Func2 called"<<endl;};
    
virtual void Func3(){cout<<"Base1 Func3 called"<<endl;};
    
int iBase1;
}
;

class  Base2
{
public:
    
virtual void Func2(){cout<<"Base2 Func2 called"<<endl;};
    
virtual void Func3(){cout<<"Base2 Func3 called"<<endl;};
    
virtual void Func4(){cout<<"Base2 Func4 called"<<endl;};
    
int iBase2;
}
;

class  Derived :  public   virtual  Base,  public    virtual  Base1,  public   Base2
{
public:
    
void Func2(){cout<<"Derived Func2 called"<<endl;};
    
void Func3(){cout<<"Derived Func3 called"<<endl;};
    
virtual void Func5(){cout<<"Derived Func5 called"<<endl;};
    
int iDerived;
}
;



1>class Derived size(32):
1> +---
1> | +--- (base class Base2)
1> 0 | | {vfptr}
1> 4 | | iBase2
1> | +---
1> 8 | {vbptr}
1>12 | iDerived
1> +---
1> +--- (virtual base Base)
1>16 | {vfptr}
1>20 | iBase
1> +---
1> +--- (virtual base Base1)
1>24 | {vfptr}
1>28 | iBase1
1> +---
1>Derived::$vftable@Base2@:
1> | &Derived_meta
1> |  0
1> 0 | &Derived::Func2
1> 1 | &Derived::Func3
1> 2 | &Base2::Func4
1> 3 | &Derived::Func5
1>Derived::$vbtable@:
1> 0 | -8
1> 1 | 8 (Derivedd(Derived+8)Base)
1> 2 | 16 (Derivedd(Derived+8)Base1)
1>Derived::$vftable@Base@:
1> | -16
1> 0 | &thunk: this-=16; goto Derived::Func2
1>Derived::$vftable@Base1@:
1> | -24
1> 0 | &Base1::Func1
1> 1 | &thunk: this-=24; goto Derived::Func2
1> 2 | &thunk: this-=24; goto Derived::Func3




菱形虚拟继承

#include  < iostream >

using   namespace  std;
class  Top
{
public:
    
virtual void Func1(){};
    
int TopValue;
}
;

class  Left:  virtual   public  Top
{
public:
    
void Func1(){};
    
virtual void Func2(){}
    
int LeftValue;
}
;

class  Right:  virtual   public  Top
{
public:
    
void Func1(){};
    
int RightValue;
}
;

class  Derived :  public  Left,  public  Right
{
public:
    
void Func1(){};
    
void Func3(){}
    
virtual void Func4(){};
    
int DerivedValue;
}
;

int  main()
{
      

               Derived d;

               Derived* pd = &d;

               Top* pt = &d;

               cout<<(int*)pt<<endl;

               cout<<(int*)pd<<endl;


    
return 0;
}


1>class Derived size(32):
1> +---
1> | +--- (base class Left)
1> 0 | | {vfptr}
1> 4 | | {vbptr}
1> 8 | | LeftValue
1> | +---
1> | +--- (base class Right)
1>12 | | {vbptr}
1>16 | | RightValue
1> | +---
1>20 | DerivedValue
1> +---
1> +--- (virtual base Top)
1>24 | {vfptr}
1>28 | TopValue
1> +---
1>Derived::$vftable@Left@:
1> | &Derived_meta
1> |  0
1> 0 | &Left::Func2
1> 1 | &Derived::Func4
1>Derived::$vbtable@Left@:
1> 0 | -4
1> 1 | 20 (Derivedd(Left+4)Top)
1>Derived::$vbtable@Right@:
1> 0 | 0
1> 1 | 12 (Derivedd(Right+0)Top)
1>Derived::$vftable@Top@:
1> | -24
1> 0 | &Derived::Func1

如上代码的输出结果表示虚基类指针的地址比派生类指针地址多了24,符合上面列出的对象内存布局。

当通过对象访问基类成员时,无论是否为虚拟继承,都可以再编译时期获得对象内存布局,直接计算出偏移量访问。
当通过指针访问基类成员时,如果是非虚拟继承,直接计算偏移量访问;
如果是虚拟继承,需要先计算出虚基类表指针的偏移地址,再根据指针找到虚基类表,取虚基类表中该项的内容,根据此内容计算虚基类对象的偏移地址,再根据成员在虚基类对象中的偏移地址访问。
所以一般说来,当从派生类中访问虚基类成员时,应该先强制转化派生类指针为虚基类指针,然后一直使用虚基类指针来访问虚基类成员变量。这样做,可以避免每次都要计算虚基类地址的开销。


虚函数表不一定在派生类对象的首地址,例如当派生类从基类虚拟继承时,派生类如果没有定义新的虚函数,那么在派生类的对象空间中,首先分布的是虚基类表,然后是派生类成员,最后是虚基类对象,虚函数表在虚基类对象开始位置。

class  Base
{
public:
    
virtual void fun(){}
}
;

class  Derived:  virtual   public  Base
{
    
void fun(){}
    
int i;
}
;


1>class Derived size(12):
1> +---
1> 0 | {vbptr}
1> 4 | i
1> +---
1> +--- (virtual base Base)
1> 8 | {vfptr}
1> +---
1>Derived::$vbtable@:
1> 0 | 0
1> 1 | 8 (Derivedd(Derived+0)Base)
1>Derived::$vftable@:
1> | -8
1> 0 | &Derived::fun

你可能感兴趣的:(虚基类、虚函数,对象内存分布)