C++多重继承下,对象布局与编译器,是否为虚拟继承都有很大关系,下面将逐一分析其中的差别,相同点为都按照类继承的先后顺序布局(类内按照虚表、成员声明先后顺序排列)。该类情况为子类按照继承顺序排列,如class C:public B,public A此种情况布局如下:

如果class B,A带有虚函数,情况又发生了变化:

考虑下面的情况:

class D {
int d;
public:
virtual void fun() {}
virtual ~D() {}
}; class E {
public:
virtual void fun() {}
virtual ~E() {}
private:
int e;
}; class F:public D,public E {
public:
void fun() {D.fun();}
virtual ~F() {}
private:
int f;
};

类F函数的布局中无虚表(vs2010,20个字节):

class F	size(20):
+---
| +--- (base class D)
0 | | {vfptr}
4 | | d
| +---
| +--- (base class E)
8 | | {vfptr}
12 | | e
| +---
16 | f
+---

g++ 编译器中包含虚表:

Class F    	size=20 align=4   	base size=20 base align=4    F (0x2ea1740) 0
vptr=((& F::_ZTV1F) + 8u)
D (0x2e76b28) 0
primary-for F (0x2ea1740)
E (0x2e76b60) 8
vptr=((& F::_ZTV1F) + 28u)

2)  当出现基类重复继承情况

如非虚拟继承,重复继承,编译器会产生告警信息:

warning: direct base 'D' inaccessible in 'G' due to ambiguity [enabled by default]

编译器产生的布局与情况1)相同,都是按照继承的顺序排列

3) 虚拟继承(非菱形继承)

基类若非包含继承同一个父类,虚拟继承实际不存在,考虑下面的继承体系:

类F继承自类D,类G虚拟继承D,F,类的布局图如下(与情况1相同):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class H
size=32 align=4
base size=4 base align=4
H (0x2372b80) 0 nearly-empty
vptridx=0u vptr=((& H::_ZTV1H) + 16u)
D (0x231b9d8) 4 virtual
vptridx=4u vbaseoffset=-12 vptr=((& H::_ZTV1H) + 40u)
F (0x2372bc0) 12 virtual
vptridx=8u vbaseoffset=-16 vptr=((& H::_ZTV1H) + 68u)
D (0x231ba10) 12
primary-for F (0x2372bc0) E (0x231ba48) 20 vptridx=12u vptr=((& H::_ZTV1H) + 88u)

vs2010 编译布局与cl -d1reportAllClassLayout multiInherit.cpp,与类G布局不同,类H这里产生了虚表:

class H	size(32):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base F)
| +--- (base class D)
12 | | {vfptr}
16 | | d
| +---
| +--- (base class E)
20 | | {vfptr}
24 | | e
| +---
28 | f
+---

4) 虚拟继承(菱形继承)

菱形继承情况如下

无论类K是否虚拟自I,J,g++产生的类布局都相似(基类都产生虚表,不过地址指向同一内存):

g++ -fdump-class-hierarchy -c multiInherit.cpp
Class K
size=16 align=4
base size=8 base align=4
K (0x237f340) 0
vptridx=0u vptr=((& K::_ZTV1K) + 12u)
I (0x237f380) 0 nearly-empty
primary-for K (0x237f340)
subvttidx=4u
D (0x231bc40) 8 virtual
vptridx=20u vbaseoffset=-12 vptr=((& K::_ZTV1K) + 56u)
J (0x237f3c0) 4 nearly-empty
subvttidx=12u vptridx=24u vptr=((& K::_ZTV1K) + 32u)
D (0x231bc40) alternative-path

类L的布局如下:

Class L
size=16 align=4
base size=4 base align=4
L (0x237f680) 0 nearly-empty
vptridx=0u vptr=((& L::_ZTV1L) + 24u)
I (0x237f6c0) 0 nearly-empty virtual
primary-for L (0x237f680)
subvttidx=16u vptridx=4u vbaseoffset=-20
D (0x231bd58) 4 virtual
vptridx=8u vbaseoffset=-12 vptr=((& L::_ZTV1L) + 48u)
J (0x237f700) 12 nearly-empty virtual
subvttidx=24u vptridx=12u vbaseoffset=-24 vptr=((& L::_ZTV1L) + 76u)
D (0x231bd58) alternative-path

VS 2010产生的类布局与是否虚拟继承有很大关系,未虚拟继承将不产生虚表且相同基类排列在子类下面而非其子类的父类下:

cl -d1reportAllClassLayout multiInherit.cpp
class K size(16):
+---
| +--- (base class I)
0 | | {vbptr}
| +---
| +--- (base class J)
4 | | {vbptr}
| +---
+---
+--- (virtual base D)
8 | {vfptr}
12 | d
+---

类L的布局产生了虚表,且重新排列了基类顺序:

class L	size(20):
+---
0 | {vbptr}
+---
+--- (virtual base D)
4 | {vfptr}
8 | d
+---
+--- (virtual base I)
12 | {vbptr}
+---
+--- (virtual base J)
16 | {vbptr}
+---

C++ 类继承的对象布局的更多相关文章

  1. 【JavaScript】类继承(对象冒充)和原型继承__深入理解原型和原型链

    JavaScript里的继承方式在很多书上分了很多类型和实现方式,大体上就是两种:类继承(对象冒充)和原型继承. 类继承(对象冒充):在函数内部定义自身的属性的方法,子类继承时,用call或apply ...

  2. C++类继承内存布局(一)

    转自:http://blog.csdn.net/jiangyi711/article/details/4890889# 一 类布局 不同的继承方式将导致不同的内存布局 1)C结构 C++基于C,所以C ...

  3. Visual C++ 8.0对象布局的奥秘:虚函数、多继承、虚拟继承(VC直接输出内存布局)

    原文:VC8_Object_Layout_Secret.html 哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleCl ...

  4. VC++对象布局的奥秘:虚函数、多继承、虚拟继承

    哈哈,从M$ Visual C++ Team的Andy Rich那里又偷学到一招:VC8的隐含编译项/d1reportSingleClassLayout和/d1reportAllClassLayout ...

  5. C++继承 派生类中的内存布局(单继承、多继承、虚拟继承)

    今天在网上看到了一篇写得非常好的文章,是有关c++类继承内存布局的.看了之后获益良多,现在转在我自己的博客里面,作为以后复习之用. ——谈VC++对象模型(美)简.格雷程化    译 译者前言 一个C ...

  6. 浅析GCC下C++多重继承 & 虚拟继承的对象内存布局

    继承是C++作为OOD程序设计语言的三大特征(封装,继承,多态)之一,单一非多态继承是比较好理解的,本文主要讲解GCC环境下的多重继承和虚拟继承的对象内存布局. 一.多重继承 先看几个类的定义: 01 ...

  7. Python类,域,方法,对象,继承

    类和对象: 是面向对象编程的两个主要方面,类创建一个新类型,而对象这个类的实例.. 域: 属于一个对象或类的变量被称为域.域有两种类型: 属于每个实例(类的对象)或属于类本身.它们分别被称为实例变量和 ...

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

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

  9. C++类继承中,基类/当前对象属性/当前对象的构造顺序

    [1]中提到,规范的派生类构造函数三个要点: 首先创建基类对象 应通过成员初始化列表,创建基类对象 应该初始化本派生类新增的成员变量 那在构造派生类实例的过程中,其基类(以及多继承的时候多个基类)/当 ...

随机推荐

  1. kotlin 练习

    Kotlin 定义函数的一些语法 fun main(args:Array<String>):Unit { val x:() -> Unit = { println("hel ...

  2. Java集合类学习笔记(各种Map实现类的性能分析)

    HashMap和Hashtable的实现机制几乎一样,但由于Hashtable是一个古老的.线程安全的集合,因此HashMap通常比Hashtable要快. TreeMap比HashMap和Hasht ...

  3. IE关闭兼容性视图

    不知道什么时候,ie8的“兼容性视图设置”变成了灰色,如图:  今天通过设置组策略,终以解决了这个问题: ie8的兼容性视图设置灰色的解决办法:运行gpedit.msc--用户配置/计算机配置--管理 ...

  4. hdu, KMP algorithm, linear string search algorithm, a nice reference provided 分类: hdoj 2015-07-18 13:40 144人阅读 评论(0) 收藏

    reference: Rabin-Karp and Knuth-Morris-Pratt Algorithms By TheLlama– TopCoder Member https://www.top ...

  5. ucenter 新增用户后 自动登录discuz 不用激活

    uc_client models user.php function add_user($username, $password, $email, $uid = 0, $questionid = '' ...

  6. 进程间通信--pipe

    管道的两种局限性: 历史上,他们是半双工的(即数据只能够在一个方向上流动). 现在某些系统也提供全双工管道,但是为了最佳的移植性,我们决不应该预先假定系统使用此特性 他们只能够在具有公共祖先的进程间使 ...

  7. JavaScript Cookies

    JavaScript Cookies 当 web 服务器向浏览器发送 web 页面时,在连接关闭后,服务端不会记录用户的信息.Cookies 的作用就是用于存储 web 页面的用户信息. Cookie ...

  8. Bpmx实施经验

    Bpmx是一个较大的平台,直接发布的话会有内存问题,经查阅一些资料,java1.5没有解决好之前版本的历史问题,所以在垃圾处理gc上有很多配置需要手动完成,之后的版本同上. Bpmx平台自带的文档中部 ...

  9. 利用python实现爬虫爬取某招聘网站,北京地区岗位名称包含某关键字的所有岗位平均月薪

    #通过输入的关键字,爬取北京地区某岗位的平均月薪 # -*- coding: utf-8 -*- import re import requests import time import lxml.h ...

  10. 2014年6月份第3周51Aspx源码发布详情

      基于知识树的多课程网络教学平台源码  2014-6-16 [VS2008]功能介绍:本平台是一个支持网上教学的网站,支持多个课程,教师可根据需要创建课程,进行课程结构.题库等的管理.   技术特色 ...