动态绑定,多态(带你从本质了解多态)

简介: 动态绑定,多态(带你从本质了解多态)

在上一章节中,我们讲述了虚函数和虚函数表,我们知道了不管在类中有多少个虚函数,都不会使类的大小扩大,在this指针中,只会多出一个虚函数表的地址,是this指针的第一个内容,在虚函数表中,函数是根据虚函数定义的顺序排列的,在这一章节中,我们将通过深入解析虚函数表,从而从本质上理解多态。


一.深入探索虚函数表

我们知道在虚函数表中存储的是该函数的地址,那么我们该如何验证?

我们可以通过函数指针的方式来调用存储在虚函数表中的函数:

#include "stdafx.h"
class Base{
public:
  int a;
  int b;
  Base(){
    a = 1;
    b = 2;
  }
  void Function_1(){
    printf("Base:Function_1...\n");
  }
  virtual void Function_2(){
    printf("Base:Function_2...\n");
  }
  virtual void Function_3(){
    printf("Base:Function_3...\n");
  }
};
int main(int argc, char* argv[])
{
  typedef void (*Function)(void);
  Base b;
  int* p;
  p = (int*)&b;
  int* function;
  function = (int*)(*p);
  Function pFn;
  for(int i=0;i<2;i++){
    pFn = (Function)*(function+i);
    pFn();
  }
  return 0;
}

这里我们通过函数指针来调用虚函数表中的地址,发现虚函数表中存的确实是虚函数的地址,且顺序是按照定义虚函数的顺序排列的。

1.单继承无函数覆盖下的虚函数表

我们知道在虚函数表中存的只有虚函数的地址,所以我们在写代码的时候不再写构造函数和析构函数,我们只写虚函数

#include "stdafx.h"
class Base{
public:
  int a;
  int b;
  virtual void Function_1(){
    printf("Base:Function_1...\n");
  }
  virtual void Function_2(){
    printf("Base:Function_2...\n");
  }
};
class Sub1:public Base{
public:
  int c;
    virtual void Sub_1(){
    printf("Sub1:Sub_1...\n");
  }
  virtual void Sub_2(){
    printf("Sub2:Sub_2...\n");
  }
};
int main(int argc, char* argv[])
{
  typedef void (*Function)(void);
  Sub1 b;
  int* p;
  p = (int*)&b;
  int* function;
  function = (int*)(*p);
  Function pFn;
  for(int i=0;i<4;i++){
    pFn = (Function)*(function+i);
    pFn();
  }
  return 0;
}

我们看看程序输出窗口:

我们观察代码就能知道,我们通过函数指针调用函数的时候,是根据虚函数表的顺序调用的,所以我们得出结论:

当继承父类的时候,父类的虚函数会在派生类虚函数的上面,且它们的顺序都为定义虚函数的顺序。

2.单继承有函数覆盖下的函数虚表
#include "stdafx.h"
class Base{
public:
  int a;
  int b;
  virtual void Function_1(){
    printf("Base:Function_1...\n");
  }
  virtual void Function_2(){
    printf("Base:Function_2...\n");
  }
};
class Sub1:public Base{
public:
  int c;
  virtual void Function_1(){
    printf("Sub1:Function_1...\n");
  }
  virtual void Sub_1(){
    printf("Sub1:Sub_1...\n");
  }
  virtual void Sub_2(){
    printf("Sub2:Sub_2...\n");
  }
};
int main(int argc, char* argv[])
{
  typedef void (*Function)(void);
  Sub1 b;
  int* p;
  p = (int*)&b;
  int* function;
  function = (int*)(*p);
  Function pFn;
  for(int i=0;i<5;i++){
    pFn = (Function)*(function+i);
    pFn();
  }
  return 0;
}

这里注意一个细节,在通过函数指针调用函数的时候,我写了一个for循坏,并且次数为5,但是在程序运行的时候,它弹出来一个窗口告诉我该地址不可访问,则说明虚函数表里的函数肯定比五个少。

我们来看看程序输出窗口:

这里打印出的函数顺序,实际上就是虚函数表里的函数顺序。

这时候,我们应该记得在课堂上,“铁男“说过一句话:”覆盖的是哪个,就在那个表里“。这里我解释一下:子类Function_1覆盖的是父类的虚函数,那么这个函数就出现在”父类的虚函数表“里(注意这里我们只是形象地称为父类的虚函数表,实际上这里只有一张虚函表哦),但是函数的具体功能是我们后定义的那个函数的功能。

相关文章
|
12月前
面向对象设计领域中的参数多态,包含多态,过载多态和强制多态
面向对象设计领域中的参数多态,包含多态,过载多态和强制多态
|
4天前
|
Java 编译器
封装,继承,多态【Java面向对象知识回顾①】
本文回顾了Java面向对象编程的三大特性:封装、继承和多态。封装通过将数据和方法结合在类中并隐藏实现细节来保护对象状态,继承允许新类扩展现有类的功能,而多态则允许对象在不同情况下表现出不同的行为,这些特性共同提高了代码的复用性、扩展性和灵活性。
封装,继承,多态【Java面向对象知识回顾①】
|
4月前
|
C++
C++一分钟之-继承与多态概念
【6月更文挑战第21天】**C++的继承与多态概述:** - 继承允许类从基类复用代码,增强代码结构和重用性。 - 多态通过虚函数实现,使不同类对象能以同一类型处理。 - 关键点包括访问权限、构造/析构、菱形问题、虚函数与动态绑定。 - 示例代码展示如何创建派生类和调用虚函数。 - 注意构造函数初始化、空指针检查和避免切片问题。 - 应用这些概念能提升程序设计和维护效率。
33 2
|
5月前
|
存储 安全 编译器
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
45 0
|
5月前
|
存储 C++
C++类与对象【多态】
C++类与对象【多态】
动态绑定,多态(带你从本质了解多态)(下)
动态绑定,多态(带你从本质了解多态)
|
编译器 C++ 开发者
C++虚函数详解:多态性实现原理及其在面向对象编程中的应用
在面向对象的编程中,多态性是一个非常重要的概念。多态性意味着在不同的上下文中使用同一对象时,可以产生不同的行为。C++是一种面向对象的编程语言,在C++中,虚函数是实现多态性的关键
157 0
【C++】多态中虚函数的底层理解
【C++】多态中虚函数的底层理解
【C++】多态中虚函数的底层理解