首先关于虚继承和普通继承的知识,我总结一下:

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问题的更多相关文章

  1. 【整理】C++虚函数及其继承、虚继承类大小

    参考文章: http://blog.chinaunix.net/uid-25132162-id-1564955.html http://blog.csdn.net/haoel/article/deta ...

  2. 虚函数列表: 取出方法 // 虚函数工作原理和(虚)继承类的内存占用大小计算 32位机器上 sizeof(void *) // 4byte

    #include <iostream> using namespace std; class A { public: A(){} virtual void geta(){ cout < ...

  3. sizeof运算符、虚函数、虚继承考点(待修改)

    参考: http://blog.csdn.net/wangyangkobe/article/details/5951248 下面的文章解释有错误,不要看.......... 记住几句话: 编译器为每个 ...

  4. C++对象模型:单继承,多继承,虚继承

    什么是对象模型 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分.对于各种支持的底层实现机制. 类中成员分类 数据成员分为静态和非静态,成员函数有静态非静态以及虚函数 clas ...

  5. C++中的虚继承 & 重载隐藏覆盖的讨论

    虚继承这个东西用的真不多.估计也就是面试的时候会用到吧.. 可以看这篇文章:<关于C++中的虚拟继承的一些总结> 虚拟基类是为解决多重继承而出现的. 如:类D继承自类B1.B2,而类B1. ...

  6. C++ 继承之虚继承与普通继承的内存分布

    仅供互相学习,请勿喷,有观点欢迎指出~ class A { virtual void aa(){}; }; class B : public virtual A { ]; //加入一个变量是为了看清楚 ...

  7. 虚继承之单继承的内存布局(VC在编译时会把vfptr放到类的头部,这和Delphi完全一致)

    C++2.0以后全面支持虚函数与虚继承,这两个特性的引入为C++增强了不少功能,也引入了不少烦恼.虚函数与虚继承有哪些特性,今天就不记录了,如果能搞了解一下编译器是如何实现虚函数和虚继承,它们在类的内 ...

  8. 从零开始学C++之虚继承和虚函数对C++对象内存模型造成的影响

    首先重新回顾一下关于类/对象大小的计算原则: 类大小计算遵循结构体对齐原则 第一个数据成员放在offset为0的位置 其它成员对齐至min(sizeof(member),#pragma pack(n) ...

  9. C++_day8_ 多重继承、钻石继承和虚继承

    1.继承的复习 1.1 类型转换 编译器认为访问范围缩小是安全的. 1.2 子类的构造与析构 子类中对基类构造函数初始化只能写在初始化表里,不能写在函数体中. 阻断继承. 1.3 子类的拷贝构造与拷贝 ...

随机推荐

  1. kafka(三)原理剖析

    一.生产者消息分区机制原理剖析 在使用Kafka 生产和消费消息的时候,肯定是希望能够将数据均匀地分配到所有服务器上.比如很多公司使用 Kafka 收集应用服务器的日志数据,这种数据都是很多的,特别是 ...

  2. Apache Unomi 远程代码执行漏洞复现(CVE-2020-13942)

    一.漏洞描述 Apache Unomi 是一个基于标准的客户数据平台(CDP,Customer Data Platform),用于管理在线客户和访客等信息,以提供符合访客隐私规则的个性化体验.在Apa ...

  3. 宝塔Linux命令

    安装宝塔 Centos安装脚本 5.7:yum install -y wget && wget -O install.sh http://download.bt.cn/install/ ...

  4. jvm 三种编译

    https://blog.csdn.net/fuxiaoxiaoyue/article/details/93497558 https://blog.csdn.net/tjiyu/article/det ...

  5. Compile-time Dependency Injection With Go Cloud's Wire 编译时依赖注入 运行时依赖注入

    Compile-time Dependency Injection With Go Cloud's Wire - The Go Blog https://blog.golang.org/wire Co ...

  6. libevent源码学习之event

    timer event libevent添加一个间隔1s持续触发的定时器如下: struct event_base *base = event_base_new(); struct event *ti ...

  7. vmware安装linux系统,自动建立没选项

    虚拟机安装CentOS自己跳过分区,直接就到最后的软件包安装了 建完系统后不用power on,建完后在edit一下系统参数,应该会看见两个cd, 有一个是vmware自己加的,把那个删除后在开机就可 ...

  8. 深入理解Java虚拟机读书笔记 -- Java内存区域

    Graal VM: Run Programs Faster Anywhere. 跨语言全栈虚拟机,可以作为"任何语言"的运行平台使用. Java内存结构 程序计数器:线程私有,较小 ...

  9. Flink-v1.12官方网站翻译-P005-Learn Flink: Hands-on Training

    学习Flink:实践培训 本次培训的目标和范围 本培训介绍了Apache Flink,包括足够的内容让你开始编写可扩展的流式ETL,分析和事件驱动的应用程序,同时省略了很多(最终重要的)细节.本书的重 ...

  10. Codeforces750E. New Year and Old Subsequence (线段树维护DP)

    题意:长为2e5的数字串 每次询问一个区间 求删掉最少几个字符使得区间有2017子序列 没有2016子序列 不合法输出-1 题解:dp i,p(0-4)表示第i个数匹配到2017的p位置删掉的最少数 ...