C++中虚函数工作原理和(虚)继承类的内存占用大小计算
来源:互联网 发布:怎样修改软件版本号 编辑:程序博客网 时间:2024/05/23 10:48
首先,说说GCC的编译器.
它实现比较简单,不管是否虚继承,GCC都是将虚表指针在整个继承关系中共享的,不共享的是指向虚基类的指针。
class A {
int a;
virtual ~A(){}
};
class B:virtual public A{
virtual ~B(){}
virtual void myfunB(){}
};
class C:virtual public A{
virtual ~C(){}
virtual void myfunC(){}
};
class D:public B,public C{
virtual ~D(){}
virtual void myfunD(){}
};
以上代码中 sizeof(A)=8,sizeof(B)=12,sizeof(C)=12,sizeof(D)=16.
解释:A中int+虚表指针。B,C中由于是虚继承因此大小为A+指向虚基类的指针,B,C虽然加入了自己的虚函数,但是虚表指针是和基类共享的,因此不会有自己的虚表指针。D由于B,C都是虚继承,因此D只包含一个A的副本,于是D大小就等于A(数据加上指向虚基类的指针)+B中的指向虚函数的指针+C中的指向虚函数的指针。
如果B,C不是虚继承,而是普通继承的话,那么A,B,C的大小都是8(没有指向虚基类的指针了),而D由于不是虚继承,因此包含两个A副本,大小为16. 注意此时虽然D的大小和虚继承一样,但是内存布局却不同。
然后,来看看VC的编译器
vc对虚表指针的处理比GCC复杂,它根据是否为虚继承来判断是否在继承关系中共享虚表指针,而对指向虚基类的指针和GCC一样是不共享,当然也不可能共享。
代码同上。
运行结果将会是sizeof(A)=8,sizeof(B)=16,sizeof(C)=16,sizeof(D)=24.
解释:A中依然是int+虚表指针。B,C中由于是虚继承因此虚表指针不共享,由于B,C加入了自己的虚函数,所以B,C分别自己维护一个虚表指针,它指向自己的虚函数。(注意:只有子类有新的虚函数时,编译器才会在子类中添加虚表指针)因此B,C大小为A+自己的虚表指针+指向虚基类的指针。D由于B,C都是虚继承,因此D只包含一个A的副本,同时D是从B,C普通继承的,而不是虚继承的,因此没有自己的虚表指针。于是D大小就等于A+B的虚表指针+C的虚表指针+B中的指向虚基类的指针+C中的指向虚基类的指针。
同样,如果去掉虚继承,结果将和GCC结果一样,A,B,C都是8,D为16,原因就是VC的编译器对于非虚继承,父类和子类是共享虚表指针的。
二、(虚)继承类的内存占用大小
首先,平时所声明的类只是一种类型定义,它本身是没有大小可言的。 因此,如果用sizeof运算符对一个类型名操作,那得到的是具有该类型实体的大小。
计算一个类对象的大小时的规律:
1、空类、单一继承的空类、多重继承的空类所占空间大小为:1(字节,下同);
2、一个类中,虚函数本身、成员函数(包括静态与非静态)和静态数据成员都是不占用类对象的存储空间的;
3、因此一个对象的大小≥所有非静态成员大小的总和;
4、当类中声明了虚函数(不管是1个还是多个),那么在实例化对象时,编译器会自动在对象里安插一个指针vPtr指向虚函数表VTable;
5、虚承继的情况:由于涉及到虚函数表和虚基表,会同时增加一个(多重虚继承下对应多个)vfPtr指针指向虚函数表vfTable和一个vbPtr指针指向虚基表vbTable,这两者所占的空间大小为:8(或8乘以多继承时父类的个数);
6、在考虑以上内容所占空间的大小时,还要注意编译器下的“补齐”padding的影响,即编译器会插入多余的字节补齐;
7、类对象的大小=各非静态数据成员(包括父类的非静态数据成员但都不包括所有的成员函数)的总和+ vfptr指针(多继承下可能不止一个)+vbptr指针(多继承下可能不止一个)+编译器额外增加的字节。
示例一:含有普通继承
class A
{
};
class B
{
char ch;
virtual void func0() { }
};
class C
{
char ch1;
char ch2;
virtual void func() { }
virtual void func1() { }
};
class D: public A, public C
{
int d;
virtual void func() { }
virtual void func1() { }
};
class E: public B, public C
{
int e;
virtual void func0() { }
virtual void func1() { }
};
int main(void)
{
cout<<"A="<<sizeof(A)<<endl; //result=1
cout<<"B="<<sizeof(B)<<endl; //result=8
cout<<"C="<<sizeof(C)<<endl; //result=8
cout<<"D="<<sizeof(D)<<endl; //result=12
cout<<"E="<<sizeof(E)<<endl; //result=20
return 0;
}
前面三个A、B、C类的内存占用空间大小就不需要解释了,注意一下内存对齐就可以理解了。
求sizeof(D)的时候,需要明白,首先VPTR指向的虚函数表中保存的是类D中的两个虚函数的地址,然后存放基类C中的两个数据成员ch1、ch2,注意内存对齐,然后存放数据成员d,这样4+4+4=12。
求sizeof(E)的时候,首先是类B的虚函数地址,然后类B中的数据成员,再然后是类C的虚函数地址,然后类C中的数据成员,最后是类E中的数据成员e,同样注意内存对齐,这样4+4+4+4+4=20。
示例二:含有虚继承
class CommonBase
{
int co;
};
class Base1: virtual public CommonBase
{
public:
virtual void print1() { }
virtual void print2() { }
private:
int b1;
};
class Base2: virtual public CommonBase
{
public:
virtual void dump1() { }
virtual void dump2() { }
private:
int b2;
};
class Derived: public Base1, public Base2
{
public:
void print2() { }
void dump2() { }
private:
int d;
};
sizeof(Derived)=32,其在内存中分布的情况如下:
class Derived size(32):+---
| +--- (base class Base1)
| | {vfptr}
| | {vbptr}
| | b1
| +---
| +--- (base class Base2)
| | {vfptr}
| | {vbptr}
| | b2
| +---
| d
+---
+--- (virtual base CommonBase)
| co
+---
示例3:
class A
{
public:
virtual void aa() { }
virtual void aa2() { }
private:
char ch[3];
};
class B: virtual public A
{
public:
virtual void bb() { }
virtual void bb2() { }
};
int main(void)
{
cout<<"A's size is "<<sizeof(A)<<endl;
cout<<"B's size is "<<sizeof(B)<<endl;
return 0;
}
执行结果:A's size is 8
B's size is 12
说明:对于虚继承,类B因为有自己的虚函数,所以它本身有一个虚指针,指向自己的虚表。另外,类B虚继承类A时,首先要通过加入一个虚指针来指向父类A,然后还要包含父类A的所有内容(它的数据,三个char,还要字节对齐)。因此是4+4+4=12。
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- OVS datapath模块分析:packet处理流程
- 安卓中的事件监听器
- windows 32 平台 安装 ENVI5.0 无法启动的解决办法
- ubuntu 12.04 NFS配置 (验证有效)
- C++:多态之重载
- C++中虚函数工作原理和(虚)继承类的内存占用大小计算
- 跨域的3种形式:
- 关联式容器
- 桑梓6号机黑苹果双系统安装日志
- 第二讲 第一次
- 数据仓库的逻辑建模之星型模式
- window下找查找占用端口的进程并手动杀死
- void
- 基因芯片(Affymetrix)分析3:获取差异表达基因