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

背景

原文链接:ordeder

关于非虚函数的成员函数的调用机制,可以参考:

成员函数的调用涉及到面向对象语言的反射机制。

虚函数表机制可以查看下面这个blog:

总结为:

其一:派生类由基类派生后,,除了从基类中继承相应的基类数据成员,如果基类有虚函数,那么派生类还构建了一个指向虚函数表的指针__vfptr,该指针指向一个函数指针数组。数组中存放了基类相应虚函数的入口地址。其二:如果派生类重写了基类的虚函数,那么编译器对应的操作为将指向虚函数表的指针__vfptr指向的函数指针数组中相应的虚函数入口地址改变为当前派生类实现的函数入口地址;其三:基类指针指向派生类后,实际上指向的是从基类派生到派生类那段成员的首地址(存放__vfptr,如果定义有虚函数),基类指针在调用虚函数的额时候,是通过查该__vfptr地址指向的函数指针数组来查找函数入口地址。由二可知,如果派生类重写了虚函数,那么以上查找的虚函数的入口地址将是派生类重写的函数的入口地址。基础知识:1. 指针的内存截断原则,即当指针指向某个内存后,指针的取值操作会依据指针的类型类读取内存值。换句话说,内存存储的01数据信息知识信息的载体,而指针才是信息的元数据。通过指针类型才能解析出具体内存中01信息的意义。2. C++的继承原则:编译器解释继承操作:会将基类的成员变量拷贝到派生类中。3. 基类指针指向派生类后,根据第1点所述,基类指针只能解析派生类中从基类拷贝到派生来的那部分内存。关于多继承的一个问题如果C同时派生了基类A和基类B,那么C的对象的内存是如何分布的呢?基类指针pa和基类指针pb对这块派生类C对象的内存所存储的内容又做和解析?或者说pa和pb指向的地址是否是C对象的首地址呢?如果是,以依据1所说,必将解析出错误的结果。pa = &cpb = &c

pa ?= pb ???

实例分析

定义两个基类A和B,用C同时继承了A和B。通过分析C对象成员的内存地址分布,来分析C是如何实现A和B的继承,以及虚函数表如何维护。

#include <stdio.h> class A{public:virtual void f(){printf("A:f\n");}virtual void g(){printf("A:g\n");}virtual void f1(){printf("A:f1\n");}int a;};class B{public:virtual void f(){printf("B:f\n");}virtual void g(){printf("B:g\n");}virtual void g1(){printf("B:g1\n");}int b;};class C : public A, public B {public:void f(){printf("C:f\n");}void g(){printf("C:g\n");}int c;};typedef void (*Func)();void main() {A a;B b;C c;B *pb = &c;A *pa = &c;puts("C对象的成员内存分布初探:");printf(" &c:\t%x\n &c.a:\t%x\n &c.b:\t%x\n &c.c:\t%x\n",&c,&c.a,&c.b,&c.c);puts("pa 与 pb");printf("pa:\t%x\npa->a:\t%x\n",pa,&pa->a);printf("pb:\t%x\npb->b:\t%x\n",pb,&pb->b);puts("C对象完整的布局");printf("pavtb:\t%x\npa->a:\t%x\n",pa,&pa->a);printf("pbvtb:\t%x\npa->a:\t%x\n",pb,&pb->b);printf("&c.c:\t%x\n",&c.c);puts("pavtb 和 pbvtb 内容分析:");int *pVtb = (int *)pa;Func funp;printf("pa->vftab[0]:\t");funp = (Func)(((int*)(*pVtb))[0]);funp();printf("pa->vftab[1]:\t");funp = (Func)(((int*)(*pVtb))[1]);funp();printf("pa->vftab[2]:\t");funp = (Func)(((int*)(*pVtb))[2]);funp();pVtb = (int *)pb;printf("pb->vftab[0]:\t");funp = (Func)(((int*)(*pVtb))[0]);funp();printf("pb->vftab[1]:\t");funp = (Func)(((int*)(*pVtb))[1]);funp();printf("pb->vftab[2]:\t");funp = (Func)(((int*)(*pVtb))[2]);funp();}调试分析:

C对象在VC中的结构图:

运行结果及分析:

C对象的成员内存分布初探:&c: 18ff24&c.a: 18ff28&c.b: 18ff30&c.c: 18ff34//1.c的首地址和c.a的首地址不是同一个地址!//2.c.a到c.b是int类型,内存分布为4个字节,但是他们之间的内存地址差为8(18ff28 – 18ff2c – 18ff30),说以c.a和c.b之间有内容!pa 与 pbpa: 18ff24pa->a: 18ff28pb: 18ff2cpb->b: 18ff30//从这个结果看来,c的首地址与pa同地址,pb的首地址和上文2所说的神秘消失的内存同地址。C对象完整的布局pavtb: 18ff24pa->a: 18ff28pbvtb: 18ff2cpa->a: 18ff30&c.c: 18ff34//其实:pa和pb指向的地址为虚函数表的指针所在的地址。pavtb 和 pbvtb 内容分析: (前文blog链接已经有详细的原理说明)pa->vftab[0]: C:fpa->vftab[1]: C:gpa->vftab[2]: A:f1pb->vftab[0]: C:fpb->vftab[1]: C:gpb->vftab[2]: B:g1

总结

从语句class C : public A, public B可以解释下图:

在泪水中浸泡过的微笑最灿烂,从迷惘中走出来的灵魂最清醒。

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

相关文章:

你感兴趣的文章:

标签云: