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. Delphi 中同类型方法的说明

    对象的方法能定义成静态(static).虚拟(virtual).动态(dynamic)或消息处理(message).请看下面 的例子: TFoo = class procedure IAmAStati ...

  2. 【PCB】【项目记录】AWG任意波形产生器

    ———————————————————————————————————————————————————————————————————— 原理图V1.0 01.top 已完成原理图绘制,但有相当错误, ...

  3. HTML 之 head

    使用 base 标签使页面中的所有标签在新窗口中打开.<!DOCTYPE html> <html> <head> <meta http-equiv=" ...

  4. Android中的自定义视图控件

    简介 当现有控件不能满足需求时,就需要自定义控件. 自定义控件属性 自定义控件首先要继承自View,重写两个构造函数. 第一个是代码中使用的: public MyRect(Context contex ...

  5. Haskell Platform (windows)

    一.下载地址:https://www.haskell.org/platform/windows.html Haskell Platform 整合了 Glasgow Haskell Compiler,W ...

  6. NodeJs使用asyncAwait两法

    async/await使用同步的方式来书写异步代码,将异步调用的难度降低到接近于0,未来必将大放异彩.然而在当下,由于标准化的缓存步伐,async/await尚在ES7的草案中.为了尝先,特试用了下面 ...

  7. 两台装有Ubuntu系统的服务器搭建VPN(一台为本地服务器,另一台为云服务器)

    我们搭建VPN采用的是openvpn,搭建过程总体需要经过三大步骤:   1.openvpn的安装与配置    2.端口转发    3.系统重启iptables规则自动生效  注意:以下所有名令在ro ...

  8. Javascript常用对象的属性和方法

    javascript为我们提供了一些非常有用的常用内部对象和方法.用户不需要用脚本来实现这些功能.这正是基于对象编程的真正目的. 在javascript提供了string(字符串).math(数值计算 ...

  9. 更新App版本的流程

    上班一年了还没有自己打包上传过APP,周五下班时项目经理手把手教了我一遍,我大致把流程在这里回顾一下: 1.首先要将svn上的代码拷贝一份到分支上,用终端操作:svn cp https://192.1 ...

  10. Android内存优化之 LruCache与DiskLruCache

    在日常的Adroid开发中我们经常遇到需要处理大量图片的地方,但Android手机的内存有限该怎么避免手机 内存溢出导致app程序oom,google提供了两种解决方式 LruCache LruCac ...