C++虚函数 虚函数表 多态 实现机制

来源:互联网 发布:华泰大智慧软件下载 编辑:程序博客网 时间:2024/06/13 22:53

继承 多态 的实现机制 内存布局(64位)

基础:

定义四个类

从上图的输出基本可以看出没有虚继承和虚函数时的继承内存布局

c1.a - c1.c这段内存中可以看成是放置了一个基类A的对象,同d1.a - d1.b ,d1.b - d1.d
而d1的地址和基类A对象的地址是一样的。所以强转为tempA可以直接进行
而d1强转为tempB需要进行偏移。当然这些都是由编译器完成的
如上可知:
(1)派生类中的内存布局是先放基类的数据成员(可以看成是放一个基类的对象),然后放自己的数据成员
(2)派生类中基类对象的内存布局是按照从左到右的继承顺序,在内存中从低到高写入数据(越左越先)。
(2)派生类对象地址强转为基类指针,会根据基类的在子类内存中位置进行适当偏移(偏移到基类对象的地址处)。

带虚函数:

上面都是最简单的继承内存布局情况。
大家都知道空类大小是1。(那是被编译器插进去的一个char ,使得这个class的不同实体(object)在内存中配置独一无二的地址。)
只有虚函数的类,因为有个指向虚函数表的指针,所以大小是8。(32位系统是4)

也就是从低到高分布如下:
虚函数表指针(指向------>虚函数表A存储----->该类虚函数访问方式)
数据成员

在单继承中子类的内存布局
那么就是
虚函数表指针指向------>虚函数表A包含----->基类和派生类的虚函数访问方式)
基类数据成员(可以看成是基类对象)
派生类数据成员
派生类和基类的虚函数合并放入新形成的虚函数表A。如果是派生类复写了基类的虚函数,就直接替换掉其中基类的虚函数。

在多继承中
虚函数表指针 指向------>虚函数表A 包含----->最左基类和派生类的虚函数访问方式)
最左基类数据成员
然后就是从左到右的基类的虚函数表指向--------->该基类虚函数表 包含-------->该基类和派生类复写的该基类虚函数的访问方式)
从左到右的基类的数据成员
......
派生类的数据成员

写了个例子,可以参考看看

输出:

其中c.a - c.b为啥占了16个字节,因为int a,与前面基类A的虚函数表指针八字节对齐,占了8个,接下来基类B的虚函数表指针占了8个;

这样就很好解释一些虚函数的问题了:
1)为什么最好基类的析构函数要为虚函数?
因为如果用基类的指针声明子类的对象,A* c = new C;这样析构c的时候是调用不到排生类的析构函数的。如果是虚函数就好办了,派生类中基类的虚函数表中的析构函数已经被重写为派生类的析构函数,所以c 访问自己的析构函数其实就是访问派生类的析构函数(会自动调用基类的析构)。
2)多态的实现
A* c = new C;派生类重写了基类的虚函数,这样c去调用虚函数就是调用自己的实现,而不会调用基类A的实现。








0 0