有一句经典的话,程序是写给人看的,顺便给机器执行。那么对于c++对象在内存中是怎么表示的。下面用win xp(32位) + vc++ 6.0对c++内存对象模型进行简明的探讨。
上面的类很简明,分别有两个虚函数一个int变量以及一个公有函数。那么请问一个A类的对象在内存的大小为多少即sizeof(A)?答案为8字节。因为一个int型变量为4字节,32位机加一个虚函数列表。那么继续看下面的单继承关系。
这里的sizeof(C)为多少?答案为12字节。在内存中即将两个结构叠合一起即m_ia+m_ic+vftables(虚函数列表),同时虚函数列表只有一张。
这里多继承的情况下sizeof(c)为多少?答案为20字节。可能为有疑问,因为m_ia+m_ib+m_ic+vftable_c(c的虚函数列表)应该为16字节,查看内存得知其实这里有两张虚函数列表,一个为B的,一个为A的。这里的答案可以自己上机得到,然后再思考为什么是这样。我们知道c++有几种类型的转换,static_cast,dynamic_cast,reinterpret_cast等。一般我们可以简单认为对于高精度向低精度转换时,只要将多余的截掉就ok啦。这种转换即保证指针的值不变,只改变所指向内存的大小,例如double a ; int *p = &a 。但是对于这里的多继承关系,如上例c继承b和a。那么将c类对象指针转换为b类指针其值不变,那么转换为a呢?答案请自己上机验证。附上程序,调试过程以及图则略:
#include<stdio.h>
#include<iostream>
using namespace std;
class A {
public:
A():m_ia(0)
{}
virtual void vfun0() ;
virtual void vfunA1();
int m_ia ;
void funA() ;
} ;
void A::vfun0()
{
cout<<" vfunA0 called "<<endl ;
}
void A::vfunA1()
{
cout<<" vfunA1 called "<<endl ;
}
void A::funA()
{
cout<<"funA called "<<endl ;
}
class B {
public:
B():m_ib(0)
{}
virtual void vfun0() ;
virtual void vfunB1() ;
int m_ib ;
void funB() ;
} ;
void B::vfun0()
{
cout<<" vfunB0 called "<<endl ;
}
void B::vfunB1()
{
cout<<" vfunB1 called "<<endl ;
}
void B::funB()
{
cout<<"funB called "<<endl ;
}
class C : public B , public A{
public:
C():m_ic(0)
{}
virtual void vfun0();
virtual void vfun1();
int m_ic ;
};
void C::vfun0()
{
cout<<" vfunC0 called "<<endl ;
}
void C::vfun1()
{
cout<<" vfunC1 called "<<endl ;
}
int main ()
{
A *pa , a ;
B *pb ,b ;
C c ;
pa = reinterpret_cast<A*>(&c) ;
printf("the pa :0x%p\n",pa);
pa->vfun0();
pa->vfunA1();
printf("pa->vfunA1:%p\n",pa->vfunA1);
printf("c.vfun1:%p\n",c.vfun1);
pb = &c ;
printf("the pb :0x%p\n",pb);
pb->vfun0();
pb->vfunB1();
printf("c size:%d\n",sizeof(c));
c.vfun1();
printf("%d\n",sizeof(c));
return 0 ;
}