• C++学习之路(九):从菱形继承引入的对象模型


    一、单继承

    class A {int a;};
    class B : public A {int b;};

    普通的单继承关系,类的大小是由其虚表指针和非静态成员函数大小决定。故上述sizeof(A)的大小为4。

    类B公有继承A,对象模型如下:

      类A的非静态成员(4字节)

      类B的非静态成员(4字节)

    故类B的大小为8字节。

    二、多继承

    class A {int a;};
    class B {int b;};
    class C : public A, public B {int c;};

    普通的多继承关系,这里和上述单继承类似。直接给出类C的对象模型如下:

      类A的非静态成员(4字节)

      类B的非静态成员(4字节)

      类C的非静态成员(4字节)

    故类C的大小为12字节。

    三、含有虚函数的普通继承

    (1)含有虚函数的类对象模型

    C++中引入了虚函数,即多态的概念。如果一个类中存在虚函数,则该类对象在被实例化时,其首地址开始的4个字节存放着虚函数表指针(vptr),vptr指向了一个虚表(理解为结构体数组),数组中的每一个位置存放着虚函数的实际地址。(ps:一个类的大小与其非成员变量有关,所以如果一个仅包含虚函数的类,其大小只有一个虚表指针,即4字节)

    class A
    {
        virtual void test(void);
        int a;
    };

    故上述类A的大小为,一个vptr加上一个成员变量,为8字节。

    (2)含有虚函数的普通单继承

    class A
    {
        virtual void test(void);
        int a;
    };
    
    class B : public A
    {
        int b;
    };

    类B直接继承类A,由于类A中存在虚函数,所以类B对象中同样也会有vptr虚表指针,并指向一个虚表,用于重写test函数。类B的对象模型如下:

      类B的vptr(4字节)  ----->  指向了一个虚表

      类A的非静态成员(4字节)

      类B的非静态成员(4字节)

    故类B的大小为12字节。(先虚表,然后是基类的成员,最后是子类的成员)

    (3)含有虚函数的多继承

    含有虚函数的多继承

    class A
    {
        virtual void test_a(void){}
        int a;
    };
    
    class B
    {
        virtual void test_b(void){}
        int b;
    };
    
    class C : public A, public B {int c;};

    类C的对象模型如下:

      类A的vptr

      A::a

      类B的vptr

      B::b

      C::c

    四、含有虚函数的类对象虚继承

    class A
    {
        virtual void test(void);
        int a;
    };
    
    class B : public virtual A
    {
       void test(void);
      virtual void test1(void);
    int b; };

    直接给出类B的对象模型如下:

      类B的vptr(4字节)    ----->  指向虚表,新增的虚函数放在自己的虚表

      类B的非静态成员(4字节)

      类A的vptr(4字节)              ------>  指向虚表,重写的虚函数放在这个虚表。保存B::test()

      类A的非静态成员(4字节)

    故类B的大小为16字节。

    五、菱形继承

    所谓菱形继承,是一种较为特殊的多继承关系,融合了多继承与虚继承。如图:

     

    结合代码如下:

    class X {};
    class Y : public virtual X {};
    class Z : public virtual X {};
    class A : public Y, public Z {};

    如果通过sizeof输出上述四个类的大小,结果为:1,4,4,8。

     (1)、空类的大小

    C++定义一个类如果是一个空类,会被编译器默认插入一个char(1个字节),从而使得该类的两个对象在内存中可以有独一无二的地址。所以上述sizeof(X)结果为1

    (2)、虚继承类的大小

    在虚继承的子类中,子类除了vptr,还会增加一种形式的指针。这个指针或者指向虚基类子对象,或者指向一个相关的表格,表格中存放的不是虚基类子对象的地址,就是其偏移量,这个指针被称为bptr。(在同时存在vptr和bptr的时候,某些编译器会将其优化,合并为一个指针)

    故上述Y和Z,自身包含指针bptr(占四个字节),继承X的非静态成员变量(占0个字节),故sizeof(Y)和sizeof(Z)结果都为4。

    (3)、类A的大小

    A是直接从Y和Z多继承而来,由于X是一个虚基类(子类都虚继承),所以在A中仅仅会存在一个X类的副本。整个A的对象结构为:

      类Y的bptr(4字节)    

      类Y的非静态成员(0字节)  

      类Z的bptr(4字节)

      类Z的非静态成员(0字节)

      类A的非静态成员(0字节)

      类X的非静态成员(0字节)

    故sizeof(X)结果为8。这里从上往下分别是Y和Z的顺序,是由类A的继承顺序决定的。

    参考:https://www.cnblogs.com/fanzhidongyzby/archive/2013/01/14/2859064.html

  • 相关阅读:
    移动端开发rem布局之less+媒体查询布局的原理步骤和心得
    前端实现文件下载方式总汇
    如何能提高CSS编写技巧?提高Web前端开发效率
    常用的CSS命名规则
    CSS背景background
    CSS盒子模型
    简单的树形菜单如何写?
    彻底掌握css动画【transition】
    首页白屏优化实践
    我来聊聊面向模板的前端开发
  • 原文地址:https://www.cnblogs.com/scu-cjx/p/8989274.html
Copyright © 2020-2023  润新知