关于虚继承的sizeof问题
首先关于虚继承和普通继承的知识,我总结一下:
1.普通继承时,无论派生类是否定义新的虚函数,基类和派生类总是共享一个虚函数表,不需要另加指向虚函数的指针,派生类只是将虚函数表中的元素改成了派生类的地址而已,虚函数表还是一个,指针数量也没有增加。
2.虚继承时,若是派生类只是继承或重写基类中虚函数,则基类和派生类是共享一个虚函数表;若派生类新定义了虚函数,则需要新加一个虚指针指向新的虚函数表。
虚继承中还有一个虚基类指针的概念,我需要在子类中多维护一个指向基类的虚基类指针
普通继承
当单继承且普通继承时:每个含有虚函数的类只有一个虚函数表,所以只需要一个虚表指针即可;
当多继承且普通继承时:一个子类有几个父类则会有几个虚函数表,所以就有和父类个数相同的虚表指针来标识;
总之,普通继承时,不需要额外增加虚函数表指针。
虚继承
当虚继承时:无论是单虚继承还是多虚继承,需要有一个虚基类表来记录虚继承关系,所以此时子类需要多一个虚基类表指针;而且只需要一个即可。
当虚继承时可能出现一个类中持有多个虚函数表的情况:无论是单虚继承还是多虚继承,
如果子类没有构造函数和析构函数,且子类中的虚函数都是在父类中出现的虚函数,这个时候不需要增加任何虚表指针;只需要像多继承那个持有父类个数的虚表指针来标识即可;
如果子类中含有构造函数或者析构函数或二者都有,则在子类中只要每出现一个父类中的虚函数则需要增加一个虚函数表指针来标识此类的虚函数表;
例子:
class A
{
private:
int a;
public:
A() {}
~A() {}
void A1() {}
void A2() {}
virtual void V1() = 0;
virtual void V2() = 0;
};
class B
{
private:
int b;
public:
void B1() {}
virtual void V1() = 0;
void B2() {}
}; class AA
{
private:
char a[2];
public:
virtual void aa() {}
};
class CC : public virtual AA
{
private:
char b[2];
char a[2];
public:
virtual void bb() {}
virtual void aa() {}
}; int main()
{
cout<<sizeof(A)<<endl;
cout<<sizeof(B)<<endl;
cout<<sizeof(AA)<<endl;
cout<<sizeof(CC)<<endl;
return 0;
}
在32位的编译器下,A的大小是8,int值+一个虚函数指针,4+4=8。B的大小是8,同样是int值+一个虚函数指针,4+4=8。AA的大小是8,2个char+一个虚函数指针,并对齐,4+4=8。CC的大小是20,这里是虚继承,并且子类CC中有自己的虚函数,所以需要一个虚函数指针,再加上CC中的元素,4个char元素,刚好是4,再多一个虚基类指针,4,另外再加上基类的数据2个char数据,对齐到4,还有基类中A的虚函数指针,所以是20.
那么如果我把virtual去掉,就变成了普通继承,这个时候子类中不需要维护虚基类指针,而且,可以和基类AA共用一个虚函数表,只需要一个虚函数指针,再加上数据,一共是12。
所以我们可以发现,虚继承中,除非子类不增加新的虚函数,不然是要多增加一个虚函数指针的,即基类和子类不共享虚函数表了。
另外一个例子:
class A{
char k[3];
public:
virtual void aa(){};
};
class B:public virtual A{
char j[3];
public:
virtual void bb(){};
};
class C:public virtual B{
char i[3];
public:
virtual void cc(){};
};
int main()
{
cout << "sizeof(A):" << sizeof(A) << endl;
/*A:虚函数指针+k
B:B虚函数指针+k+A虚函数指针+j+虚基类指针
C:C虚函数指针+i+B虚函数指针+B虚基类指针+k+j+C虚基类指针+A虚函数指针*/
cout << "sizeof(B):" << sizeof(B) << endl;
cout << "sizeof(C):" << sizeof(C) << endl;
system("PAUSE");
return 0;
}
32位编译器下,输出依次是8,20,32。
关于虚继承的sizeof问题的更多相关文章
- 【整理】C++虚函数及其继承、虚继承类大小
参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/deta ...
- 虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte
#include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout < ...
- sizeof运算符、虚函数、虚继承考点(待修改)
参考: http://blog.csdn.net/wangyangkobe/article/details/5951248 下面的文章解释有错误,不要看.......... 记住几句话: 编译器为每个 ...
- C++对象模型:单继承,多继承,虚继承
什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...
- C++中的虚继承 & 重载隐藏覆盖的讨论
虚继承这个东西用的真不多.估计也就是面试的时候会用到吧.. 可以看这篇文章:<关于C++中的虚拟继承的一些总结> 虚拟基类是为解决多重继承而出现的. 如:类D继承自类B1.B2,而类B1. ...
- C++ 继承之虚继承与普通继承的内存分布
仅供互相学习,请勿喷,有观点欢迎指出~ class A { virtual void aa(){}; }; class B : public virtual A { ]; //加入一个变量是为了看清楚 ...
- 虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)
C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内 ...
- 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响
首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...
- C++_day8_ 多重继承、钻石继承和虚继承
1.继承的复习 1.1 类型转换 编译器认为访问范围缩小是安全的. 1.2 子类的构造与析构 子类中对基类构造函数初始化只能写在初始化表里,不能写在函数体中. 阻断继承. 1.3 子类的拷贝构造与拷贝 ...
随机推荐
- 24V降压3.3V芯片,低压降线性稳压器
PW6206系列是一款高精度,高输入电压,低静态电流,高速,低压降线性稳压器具有高纹波抑制.在VOUT=5V&VIN=7V时,输入电压高达40V,负载电流高达300mA,采用BCD工艺制造.P ...
- java.net.NoRouteToHostException: 没有到主机的路由
今天在配置Jenkins 的云服务器的时候提示:java.net.NoRouteToHostException: 没有到主机的路由,网上查到的没有主机路由问题提到的大多是防火墙问题. 查看防火墙状态: ...
- Python赋值、浅复制和深复制
Python赋值.浅复制和深复制 首先我们需要知道赋值和浅复制的区别: 赋值和浅复制的区别 赋值,当一个对象赋值给另一个新的变量时,赋的其实是该对象在栈中的地址,该地址指向堆中的数据.即赋值后,两 ...
- Lucene 查询原理 传统二级索引方案 倒排链合并 倒排索引 跳表 位图
提问: 1.倒排索引与传统数据库的索引相比优势? 2.在lucene中如果想做范围查找,根据上面的FST模型可以看出来,需要遍历FST找到包含这个range的一个点然后进入对应的倒排链,然后进行求并集 ...
- C/C++ Lua通信
C/C++和Lua是如何进行通信的? http://www.luachina.cn/?post=38 2015-12-28 为了实现Lua和其他语言之间的通信,Lua虚拟机为C/C++提供了两个特性: ...
- QT串口助手(四):数据发送
作者:zzssdd2 E-mail:zzssdd2@foxmail.com 一.前言 开发环境:Qt5.12.10 + MinGW 实现的功能 串口数据的发送 ascii字符与hex字符的相互转换 自 ...
- POJ2029 二维线段树
Get Many Persimmon Trees POJ - 2029 Seiji Hayashi had been a professor of the Nisshinkan Samurai Sch ...
- python基础学习2 函数变量与赋值
学习的第一步,就是通常的输出函数:print() 1.基础主要学习了变量的赋值,变量名可以疑字母.数字.下划线(_)进行命名,但是不能以数字开头 变量的赋值:变量名 = 变量值 2.模块的导入,和调用 ...
- Linux slabtop命令——显示内核片缓存信息
Linux内核需要为临时对象如任务或者设备结构和节点分配内存,缓存分配器管理着这些类型对象的缓存.现代Linux内核部署了该缓存分配器以持有缓存,称之为片.不同类型的片缓存由片分配器维护. slabt ...
- 关闭Linux - centos7的防火墙
关闭Centos7的防火墙 在每台虚拟机上分别执行以下指令: systemctl stop firewalld.service #停止firewall systemctl disable firewa ...