【C++】【虚函数表】通过虚函数实现多态的机制和原理

1、一个对象模型的内存布局

在C++中,对象模型的内存布局通常包含三个部分:虚函数表指针、成员变量和填充字节。

|------------------|
|  vptr            |   <---- 虚函数表指针
|------------------|
|  member1         |   <---- 成员变量1
|------------------|
|  member2         |   <---- 成员变量2
|------------------|
|  padding         |   <---- 填充字节
|------------------|

  • 虚函数表指针是一个指向虚函数表的指针,虚函数表存储了类中虚函数的地址。这个指针通常是对象中的第一个成员,如果类没有定义任何虚函数,则不会有虚函数表指针。
  • 成员变量是类定义中声明的变量,它们按照定义顺序存储在对象中。
  • 填充字节是为了内存对齐而添加的额外字节,以便于硬件的访问和操作。填充字节通常是由编译器自动添加的,以使得对象的地址是某个特定大小的整数倍。

2、虚函数表的结构

虚函数表(virtual table)是一个由编译器自动生成的数据结构,用于实现C++中的多态机制。每个含有虚函数的类都有一个虚函数表,该表包含了该类的虚函数的地址。

子类继承父类的虚函数表的方式是复制一份。存在虚函数的类,都有自己的虚函数表,不与其他类共用。

对于一个含有虚函数的类,其虚函数表通常会被放置在该类的编译单元中的静态存储区域(如 .rodata 或 .data 段)。每个对象的vptr 通常被放置在对象的最开始位置,也就是该对象的地址。

下面是一个简单的虚函数表结构的示例:

+--------+
|  vptr  |  --> 虚函数表地址
+--------+
|  data  |
+--------+

虚函数表:

+-----------------------+
| Animal::speak()        |
+-----------------------+
| Animal::move()         |
+-----------------------+
| Dog::speak()           |
+-----------------------+
| Dog::move()            |
+-----------------------+
| Dog::fetch()           |
+-----------------------+

在上面的示例中,Animal 和 Dog 都有虚函数,它们各自有一个虚函数表。vptr 指针指向虚函数表的地址。Animal 的虚函数表包含 Animal::speak() 和 Animal::move() 两个函数的地址,Dog 的虚函数表包含 Dog::speak()、Dog::move() 和 Dog::fetch() 三个函数的地址。

3、如何实现多态?

通过上面的例子,可以很好地解释 C++ 中多态的原理。假设有如下的代码:

Animal* animalPtr = new Dog;
animalPtr->speak();  // 调用 Dog::speak()
animalPtr->move();   // 调用 Dog::move()
  1. 在上面的代码中,首先创建了一个 Dog 对象,并将其赋值给一个指向 Animal 的指针。由于 Animal 类中的函数 speak() 和 move() 是虚函数,因此它们被声明为虚函数,并放置在 Animal 类的虚函数表中。在 Dog 类中重新实现了这两个虚函数,并添加了一个新的虚函数 fetch(),这些函数的地址都被放置在 Dog 类的虚函数表中。
  2. 当执行 animalPtr->speak() 时,实际上是其类对象内存布局中 vptr 指针是指向 Dog 类的虚函数表,因此调用的实际上是 Dog::speak() 函数。
  3. 在 C++ 中,由于虚函数表的存在,通过基类指针或引用调用虚函数时,会根据实际对象的类型动态地绑定到该类型的虚函数,这就是多态的实现机制。在上面的示例中,由于 animalPtr 的实际类型是 Dog,因此调用的是 Dog 类中的虚函数,而不是 Animal 类中的虚函数。

需要注意的是,为了使函数能够动态绑定到正确的虚函数,每个类必须具有自己的虚函数表。如果没有虚函数,或者虚函数没有被重写,那么该类将没有虚函数表。此外,如果派生类中重新定义了基类中的虚函数,那么它必须使用 virtual 关键字进行声明,才能将其添加到虚函数表中,从而实现多态。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值