多重虚继承下的对象内存布局

《深入C++对象模型》绝对是一本值得深读的一本书,书里多次出现一句话,“一切常规遇见虚继承,都将失效”。这是一个有趣的问题,因为C++标准容忍对象布局的实现有较大的自由,出现了各编译器厂商实现的方式不同。

今天谈谈visual studio2013多重虚继承下对象布局。有错不要客气,不要吝啬你的留言,请直接开喷。

class y和class z都是从class x虚继承来的子类(也叫派生类),class A是class y和class z的多重继承子类。为了简化问题,下面的data member都是none_static data member。不提static data member是为了描述起来简单~

#include<iostream>
class x
{
public:
    int _x;
};
class y : public virtual x
{
public:
    int _y;
};
class z : public virtual x
{
public:
    int _z;
};
class A:public z,public y
{
public:
    int _a;
};
    int main()
{
    std::cout <<"sizeof(x): "<< sizeof(x) << std::endl;
    std::cout <<"sizeof(y): "<< sizeof(y) << std::endl;
    std::cout <<"sizeof(A): "<< sizeof(A) << std::endl;
    std::cout <<"&A::_x :"<< &A::_x << std::endl;;
    std::cout << "&A::_y :" << &A::_y << " " << std::endl;
    std::cout << "&A::_z :" << &A::_z << " " << std::endl;
    std::cout <<"&A::_a :"<< &A::_a << " " << std::endl;

    getchar();
    return 0;
}

输出:

sizeof(x): 4
sizeof(y): 12
sizeof(A): 24

sizeof(x)和sizeof(y)的结果在我们的意料中:x中没有虚函数,所以sizeof(x) = sizeof(int)。class y虚继承自class x,它需要一个指针指向类似于virtual base table的指针(下面简称vbptr),指向自己实际的x对象地址,所以sizeof(y)=2*sizeof(int) + sizeof(vbptr),我使用32位编译器生成的代码,所以sizeof(y)=12.

按照我开始的料想,sizeof(A)应该等于sizeof(int)*4(分别是:_x,_y,_z,_a) + sizeof(vbptr)=20。这里的vbptr指向了唯一的x实例。

(注意啦,我要开始了)

C++标准中有规定,子类的对象模型中,应该保持父类对象的布局。在上面,我们说了sizeof(y)=2*sizeof(int) + sizeof(vbptr),bingo~,这里的sizeof(A)就是sizeof(y) + sizeof(z)=24

而vbptr因为出在了两个父类中,已经不需要额外的一个vbptr指向了。

你是不是要问这个vbptr(虚基类表指针)到底是干什么的?

我们知道虚继承的父类x,无论它被派生多次,它在最后多重继承的子类中,只存在一份实例。这样可能出现这样的情况:

y object_y;

A object_A;

object_A._x =1;

object_y = object_A;//这里发生类对象的剪切,将子类中的非父类成员剪去,剩下的赋给父类。

可能你还没有明白,没关系,我再啰嗦一句~

抱歉没有很好的绘图工具,我们使用“|“来隔开内存中的成员。

object_y 的内存布局应该是这样的:vbptr | _x | _y,注意先实例化父类,再实例化子类。

而,object_A的内存布局应该是这样的:vbptr_z | _z | vbptr_y | _y | _a | _x,注意我们多重继承的先后顺序:class A:public z,public y,这个顺序决定了z的对象布局在y对象前面。因为虚继承的原因,我们对象布局发生了改变:先实例化不共享部分(也就是class y和class z中除了class x的成员),这个部分依然是先父类再子类。最后才是虚继承的公共部分:class x的成员。

我们发现,直接进行内存布局的剪切行不通(红色部分),这需要编译器介入,但编译器也不是神仙啊,它需要信息去找_x在哪里,这就是vbptr中存的信息——它告诉编译器去哪里找_x,这里的信息可能是偏移,也可能是指针。为什么是表呢?而不是直接指向成员的指针呢?因为,如果你再虚继承几个父类,你的这个指针会越来越多,对象的规模也就越来越大,这不能容忍。使用表可以始终只占一个指针大小,只是需要间址查询,但是这完全可以由编译器优化之。

时间: 2024-07-29 10:36:05

多重虚继承下的对象内存布局的相关文章

面向对象--多继承&amp;派生类对象内存布局分析&amp;各基类指针所指向的位置分析

背景 原文链接:ordeder  http://blog.csdn.net/ordeder/article/details/25477363 关于非虚函数的成员函数的调用机制,可以参考: http://blog.csdn.net/yuanyirui/article/details/4594805 成员函数的调用涉及到面向对象语言的反射机制. 虚函数表机制可以查看下面这个blog: http://blog.csdn.net/haoel/article/details/1948051 总结为: 其一

【转载】图说C++对象模型:对象内存布局详解

原文: 图说C++对象模型:对象内存布局详解 正文 回到顶部 0.前言 文章较长,而且内容相对来说比较枯燥,希望对C++对象的内存布局.虚表指针.虚基类指针等有深入了解的朋友可以慢慢看.本文的结论都在VS2013上得到验证.不同的编译器在内存布局的细节上可能有所不同.文章如果有解释不清.解释不通或疏漏的地方,恳请指出. 回到顶部 1.何为C++对象模型? 引用<深度探索C++对象模型>这本书中的话: 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各种支持的底层

c++ 对象内存布局详解

今天看了的,感觉需要了解对象内存的问题. 1.何为C++对象模型? 引用<深度探索C++对象模型>这本书中的话: 有两个概念可以解释C++对象模型: 语言中直接支持面向对象程序设计的部分. 对于各种支持的底层实现机制. 直接支持面向对象程序设计,包括了构造函数.析构函数.多态.虚函数等等,这些内容在很多书籍上都有讨论,也是C++最被人熟知的地方(特性).而对象模型的底层实现机制却是很少有书籍讨论的.对象模型的底层实现机制并未标准化,不同的编译器有一定的自由来设计对象模型的实现细节.在我看来,对

对象内存布局 (13)——上一篇的纠正

下面来看看虚基类对对象内存布局的影响.虚基类的主要作用就是在所有的派生类中,保留且仅保留一份虚基类的suboject. #include <iostream> using namespace std; class Base { public: int m_base; Base():m_base(20){} virtual void vfBase_1() { cout << "This is in Base::vfBase_1()" << endl;

C++ 对象的内存布局—— 虚继承下的虚函数

C++ 对象的内存布局(下)这篇文章的"单一虚拟继承"和"钻石型虚拟继承"时的类内存布局讲得不太清楚,我有一处疑问,我用的是VS2005.因此记录一下. 类继承图例如以下: 这里:类B被类B1和B2虚拟继承,而B1和B2同一时候被D继承. B1的f().B2的f()覆盖了B的f(): D的f()覆盖了B1的f(),D的f1()覆盖了B1的f1() D的f()覆盖了B2的f(),D的f2()覆盖了B2的f2() 类代码例如以下: class B { public: i

C++学习笔记----4.5 C++继承时的对象内存模型

推荐阅读:http://blog.csdn.net/randyjiawenjie/article/details/6693337 最近研究了一下,C++继承的内存对象模型.主要是读了读http://blog.csdn.net/haoel/article/details/3081328(C++ 对象的内存布局).很推荐这篇文章. 对这篇文章做了做总结.本文的大部分内容来自于这篇文章中的总结http://blog.csdn.net/haoel/article/details/3081328(C++

C++对象内存布局 (二)

在上一篇文章中讨论了C++单一一般继承的对象内存布局http://www.cnblogs.com/uangyy/p/4621561.html 接下来继续讨论第二种情况: 2.单一的虚拟继承:有成员变量,有虚函数和虚函数的覆盖,虚拟继承. 我们假设下面这样一种继承关系 源码如下: #include <iostream> using namespace std; class Parent { public: int iparent; Parent() : iparent(10) {} virtua

对象内存布局 (14)

前言 07年12月,我写了一篇<C++虚函数表解析>的文章,引起了大家的兴趣.有很多朋友对我的文章留了言,有鼓励我的,有批评我的,还有很多问问题的.我在这里一并对大家的留言表示感谢.这也是我为什么再写一篇续言的原因.因为,在上一篇文章中,我用了的示例都是非常简单的,主要是为了说明一些机理上的问题,也是为了图一些表达上方便和简单.不想,这篇文章成为了打开C++对象模型内存布局的一个引子,引发了大家对C++对象的更深层次的讨论.当然,我之前的文章还有很多方面没有涉及,从我个人感觉下来,在谈论虚函数

vs查看虚函数表和类内存布局

虚继承和虚基类 虚继承:在继承定义中包含了virtual关键字的继承关系:     虚基类:在虚继承体系中的通过virtual继承而来的基类,需要注意的是:class CSubClass : public virtual CBase {}; 其中CBase称之为CSubClass的虚基类,而不是说CBase就是个虚基类,因为CBase还可以不不是虚继承体系中的基类. vs中如何查看内存布局: . 打开"Visual Studio Command Prompt (2010)" 使用cl命