多继承(虚继承)派生类对象内存结构

在这里谈一下虚继承。前面写过派生类对象的内存结构,都是基于VS2010编译器的,不同的编译器对于继承的处理不同,但本质都是一样的。

虚继承是解决共享基类问题的。例如在菱形继承中

如果不使用虚继承,基类A在D中会有两个,这不仅浪费内存,还会造成歧义。使用虚继承就可以解决基类共享的问题。

要想在派生类中共享基类(例如在D对象中只有一个A对象,这时候D对象中的B对象和C对象都可以查找到A,而不是在B对象和C对象中各含有一个A对象)。

先看下面一个例子:

#include<iostream>
using namespace std;
class A{
public:
	A():a(10){};
	int a;
};
class B: public virtual A{
public:
	B():b(20){};
	int b;
};
class C:public virtual A{
public:
	C():c(30){};
	int c;
};
class D:public B, public C{
public:
	D():d(40){};
	int d;
};
int main()
{
	cout<<"A size is "<<sizeof(A)<<endl;
	cout<<"B size is "<<sizeof(B)<<endl;
	cout<<"C size is "<<sizeof(C)<<endl;
	cout<<"D size is "<<sizeof(D)<<endl;
	return 0;
}

VS2010运行结果如下:

A size is 4

B size is 12

C size is 12

D size is 24

之前在网上看过一些文章,解决虚继承共享对象的一个方案就是在虚函数表中添加共享对象的相对偏移。我们把D类对象各个部分打印出来看一下:

D d;

	int **p=(int **)&d;
	cout<<"以4字节为单位,分成5部分:"<<endl;
	for(int i=0; i<6; i++)
		cout<<"第"<<i<<"个部分的值"<<p[i]<<endl;

以4字节为单位,分成5部分:

第0个部分的值013078B0

第1个部分的值00000014

第2个部分的值013078A4

第3个部分的值0000001E

第4个部分的值00000028

第5个部分的值0000000A

可以看出第0部分和第2部分不是成员变量,其实是虚函数表指针,在虚函数表中记录着第5部分(就是虚基类A对象)相对其他对象的偏移。第0部分记录着相对对象B和相对对象D的偏移(可以看出偏移20个字节),第2部分记录着相对C对象的偏移(偏移12个字节),可以查看一下:

cout<<p[0][1]<<endl;
	cout<<p[2][1]<<endl;

输出的结果就是20和12。

至于为什么在虚函数表的第一个位置,我还没搞懂,不过我想应该是不同的编译器有不同的安排,但是它们的目的都是共享虚基类A对象。

在D中查找虚基类,首先找到虚函数表,然后找到偏移的大小,之后才能找到虚基类对象,可见虚基类降低了效率。

时间: 2025-02-01 15:46:48

多继承(虚继承)派生类对象内存结构的相关文章

面向对象--多继承&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++由于基类指针可以指向不同的派生类对象,因此当赋予基类指针不同的地址时,要注意之前的派生类对象的内存释放. int main(){ Parent* ptr = new Child1; Child2 myChild2; Child3 myChild3; ptr->show(); delete ptr; //位置1 ptr = &myChild2; ptr->show(); delete ptr; //位置2 ptr = &myChild3; ptr->show(); d

C++继承详解之三——菱形继承+虚继承内存对象模型详解vbptr(1)

在我个人学习继承的过程中,在网上查阅了许多资料,这些资料中有关菱形继承的知识都是加了虚函数的,也就是涉及了多态的问题,而我在那个时候并没有学习到多态这一块,所以看很多资料都是云里雾里的,那么这篇文章我想以我自己学习过程中的经验,由简到较难的先分析以下菱形继承,让初学者先对这个问题有一点概念,在后面会由浅入深的继续剖析. 本篇文章不会涉及到多态也就是虚函数的菱形继承,在后面的文章更新中,我会慢慢把这些内容都加进去. 菱形继承(也叫钻石继承)是下面的这种情况: 对应代码如下: #include <i

sizeof 和类继承 虚继承 求类大小

代码: #include <iostream> using namespace std; /* class a{ float k; // 4字节 virtual void foo(){} //有一个4字节的指针指向自己的虚函数表 }; class b : virtual public a{ virtual void f(){} }; 有这样的一个指针vptr_b_a,这个指针叫虚类指针,也是四个字节:还要包括类a的字节数,所以类b的字节数就求出来了. 运行结果: 8 16 */ /* clas

基类中定义的虚函数在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型及参数的先后顺序,都必须与基类中的原型完全相同 but------&gt; 可以返回派生类对象的引用或指针

您查询的关键词是:c++primer习题15.25 以下是该网页在北京时间 2016年07月15日 02:57:08 的快照: 如果打开速度慢,可以尝试快速版:如果想更新或删除快照,可以投诉快照. 百度和网页 http://bbs.csdn.net/topics/380238133 的作者无关,不对其内容负责.百度快照谨为网络故障时之索引,不代表被搜索网站的即时页面. 首页 精选版块 移动开发 iOS Android Qt WP 云计算 IaaS Pass/SaaS 分布式计算/Hadoop J

C++虚函数的陷阱--派生类对象的基类函数调用基类虚函数出错

<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">最近写程序的时候发现,派生类对象的基类函数如果调用基类虚函数,则它将调用的是派生类的对应函数,而不是我想调用的基类的对应函数.</span> 下面用一个例子来说明: //基类 class Base { public: void funA(); virtual void fun

为什么基类指针和引用可以指向派生类对象,但是反过来不行?

为什么基类指针和引用可以指向派生类对象,但是反过来不行? 基类指针和引用 BaseClass *pbase = NULL; DerivedClass dclass; pbase = & dclass; 基类指针和引用可以指向派生类对象,但是无法使用不存在于基类只存在于派生类的元素.(所以我们需要虚函数和纯虚函数) 原因是这样的: 在内存中,一个基类类型的指针是覆盖N个单位长度的内存空间. 当其指向派生类的时候,由于派生类元素在内存中堆放是:前N个是基类的元素,N之后的是派生类的元素. 于是基类的

C++类对象内存布局(四)

测试系统:Windows XP 编译器:VS2008 (四) 虚继承的情况: 如果说没有虚函数的虚继承只是一个噩梦的话,那么这里就是真正的地狱.这个C++中最复杂的继承层次在VC上的实现其实我也没有完全理解,摸爬滚打了一番也算得出了微软的实现方法吧,至于一些刁钻的实现方式我也想不到什么理由来解释它,也只算是知其然不知其所以然吧. 也还是从最简单的开始,我们分2个阶段来探讨.一个是有虚函数的派生类虚继承了没有虚函数的基类的情况,一个情况是有虚函数的派生类虚继承了有虚函数的基类的情况. 从第一个开始

基类,派生类,内存分配情况?.xml

pre{ line-height:1; color:#1e1e1e; background-color:#d2d2d2; font-size:16px;}.sysFunc{color:#627cf6;font-style:italic;font-weight:bold;} .selfFuc{color:#800080;} .bool{color:#d2576f;} .condition{color:#000080;font-weight:bold;} .key{color:#000080;} .