C++ 继承关系下的对象布局

背景

C++ 中最基本的存储单位是字节,C++ 中所有的数据都是由对象组成的,每一个对象都包含了一个或多个内存位置;普通类的内存大小为其所有非静态成员变量内存大小之和;为提高效率内存中各个数据类型需要按照一定的规则在内存空间上进行排列,而不是挨个的顺序排列。

单一继承不带虚函数

一个派生类对象可以认为是两部分构成:一是继承的基类对应子对象;二是派生类自身定义的成员对象。

class  classA
{
public:
   char  x;
   short  y;
};
class classB : public classA
{
public:
  int    c;
};
int main()
{
  cout << "classA 大小:" << sizeof(classA) << endl;
  cout << "classB 大小:" << sizeof(classB) << endl;
  
  system("pause");
  return 0;
}

代码运行结果如下:
在这里插入图片描述

继承关系下的 this 指针

派生类继承的基类子对象的指针和派生类自身的指针相同:

class  classA
{
public:
   char  x;
   short  y;
public:
  classA()
  {
    cout << "classA() this " << this << endl;
  }
};
class classB : public classA
{
public:
  int    c;
  classB()
  {
    cout << "classA() this " << this << endl;
  }
};
int main()
{
  classB b;
  
  system("pause");
  return 0;
}

代码运行结果如下:
在这里插入图片描述

通过 VS 查看类的布局如下:
在这里插入图片描述

包含虚函数的对象布局

当类中存在虚函数时,编译器在编译时会给该类生成一个虚函数表,虚函数表是一个函数指针数组;并且在该类的每个对象中都会生成虚函数表指针 vptr,用于指向虚函数表。

class  classA
{
public:
   char  x;
   short  y;
public:
  classA() = default;  
  virtual ~classA() = default;
  virtual void Virtualfunc()
{
    cout <<" Virtualfunc 执行" << endl;
  }
};

通过 VS 查看类的布局如下:
在这里插入图片描述

如上图所示,类大小为 8 个字节,比之前增加了 4 个字节;类的前 4 个字节就是虚函数表指针,指向虚函数表的地址。
通过 VS 查看虚函数表的布局如下:
在这里插入图片描述

如上图所示,虚函数按照声明的顺序保存在虚函数表中。

单一继承不包含覆盖虚函数

class  classA
{
public:
   char  x;
   short  y;
public:
  classA() = default;  
  virtual ~classA() = default;
  virtual void Virtualfunc()
{
    cout <<" Virtualfunc 执行" << endl;
  }
};
class classB : public classA
{
public:
  int    c;
  classB() = default;
  ~classB() = default;
};

通过 VS 查看类的布局如下:
在这里插入图片描述

如上图所示,类大小为 8 个字节,比之前增加了 4 个字节;类的前 4 个字节就是虚函数表指针,指向虚函数表的地址。
通过 VS 查看虚函数表的布局如下:

在这里插入图片描述

如上图所示,classB 中的 Virtualfunc 函数还是 基类 classA 中的函数。

单一继承包含覆盖虚函数

class  classA
{
public:
   char  x;
   short  y;
public:
  classA() = default;  
  virtual ~classA() = default;
  virtual void Virtualfunc()
{
    cout <<" Virtualfunc A执行" << endl;
  }
};
class classB : public classA
{
public:
  int    c;
  classB() = default;
  ~classB() = default;
  void Virtualfunc()
{
    cout << " Virtualfunc B执行" << endl;
  }
};

通过 VS 查看虚函数表的布局如下:
在这里插入图片描述

如上图所示,此时 classB 中的 Virtualfunc 函数还是 已经是 classB 中的函数。

通过虚表指针调用虚函数

虚函数表指针位于类最前面的位置,通过虚函数表指针可以遍历表中的虚函数指针,从而调用虚函数。

classB * b = new classB;
int * ptr = reinterpret_cast<int *>(b);//指向内存中前 4 个 字节
int * vptr = reinterpret_cast<int *>(*ptr);// *ptr 即虚函数表的地址,指向整个数组typedef void(*func)();
func funcA = reinterpret_cast<func>(vptr[1]);
func funcB = reinterpret_cast<func>(vptr[2]);funcA();
funcB();

代码运行结果如下:
在这里插入图片描述

考虑继承多个基类时:

classB * b = new classB;
 int ** vtable = reinterpret_cast<int **>(b);//考虑到多继承、多个虚表
  
 typedef void(*func)();
 func funcA = reinterpret_cast<func>(vtable[0][1]);
 func funcB = reinterpret_cast<func>(vtable[0][2]);
  
 funcA();
 funcB();

在这里插入图片描述

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值