zoukankan      html  css  js  c++  java
  • 继承(五)

    在研究之前,先来回忆一下类/对象大小的计算

    • 类大小计算遵循前面学过的结构体对齐原则。
    • 类的大小与数据成员有关与成员函数无关。
    • 类的大小与静态数据成员无关。
    • 虚继承对类的大小的影响
    • 虚函数对类的大小的影响

    前面三点在之前已经介绍过了,这节主要是研究第四点,而第五点会在未来进行探讨,在探讨虚继承对内存模型的影响时,需要了解一下“虚基类表”:

    • virtual base table
      本类地址与虚基类表指针地址的差。
      虚基类地址与虚基类表指针地址的差。
    • virtual base table pointer(vbptr)

     下面先依照上面的类图的结构来构建数据模型,来细细探讨:

    #include <iostream>
    using namespace std;
    
    class BB {
    public:
        int bb_;
    };
    
    class B1 : virtual public BB {
    public:
        int b1_;
    };
    
    class B2 : virtual public BB {
    public:
        int b2_;
    };
    
    class DD : public B1, public B2 {
    public:
        int dd_;
    };
    
    int main(void) {
    
        return 0;
    }

    这里数据成员都是整型,就不会受结构体对齐的影响,比较容易算出它的大小。下面分别来打印一下各个类的大小:

    编译运行:

    可见由于有了虚继承,则并非按我们的预想来计算类大小了,下面来推导一下这种情况的内存模型B1和DD,首先来推导一下B1类的内存模型:

    编译运行:

    打印出地址之后,下面来进行推导:

    根据vbtl的存放规则来计算一下它里面的值是多少?

    这样B1的内存模型就推导出来了,口说无评,下面用代码主要来论证上面画的内存模型中的vbtl存放的是对的:

    编译运行:

    刚好论证了之前推断的模型是正确的,所以可以看到B1类的大小为12个字节,因为第一个字节是空出来存放虚基类表指针的。

    用这种方法再来推断一下DD类的内存模型:

    编译运行:

    打印地址之后下面来推导:

    那先来计算填充一下虚基类表中的值:

    下面用代码来验证一下:

    编译运行:

    论证了结果是正确的,目前还未学习到虚函数,如果有了它内存模型会更加复杂,因为虚函数会有虚表指针,它指向虚表,这个在之后再来探讨。

    想一下为什么有了虚继承内存模型会变得如此复杂?

    先从"virtual"这个单词的意思来理解:它是存在的、共享的、间接的,拿DD这个派生类来说,虚基类BB是存在的;对DD数据成员来说是共享的,BB不会创建两份;对于DD对象要访问BB数据成员需要间接访问,其间接访问就体现在虚基类表指针了,如下:

    对于DD来说,如果要找到BB,需要通过B1或B2的虚基类表的偏移位置最终找到BB:

    说到这,抛出一个问题:dd对象要访问bb这个数据成员,是直接访问还是间接访问呢?

    实际上如果是对象访问的话还是直接访问,因为内存模型在编译时刻已经决定了,但是,如果通过指针来访问情况就不一样了:

    为什么说是间接访问呢?DEBUG看下地址便知:

    关于这节比较绕,需细细体会~

  • 相关阅读:
    HTML转换成word文档
    泛型的导入导出的公用方法
    封装的分页jq
    用泛型做的分页类
    EntityFramework小知识
    泛型连接数据库
    在博客园看的一些小技巧
    渗透之路基础 -- SQL注入
    第一篇博客就这么开始了?!稍微介绍一下。
    正则基础用法
  • 原文地址:https://www.cnblogs.com/webor2006/p/5621825.html
Copyright © 2011-2022 走看看