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

posted on 2018-05-04 10:53  chenjx_ucs  阅读(498)  评论(0编辑  收藏  举报

导航