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

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

在上一章节中,我们讲述了虚函数和虚函数表,我们知道了不管在类中有多少个虚函数,都不会使类的大小扩大,在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覆盖的是父类的虚函数,那么这个函数就出现在”父类的虚函数表“里(注意这里我们只是形象地称为父类的虚函数表,实际上这里只有一张虚函表哦),但是函数的具体功能是我们后定义的那个函数的功能。

相关文章
|
7月前
|
存储 C++ 容器
第十四章:C++虚函数、继承和多态详解
第十四章:C++虚函数、继承和多态详解
65 0
|
2月前
多态和动态绑定的区别是什么?
【10月更文挑战第14天】多态和动态绑定是面向对象编程中两个重要的概念,但它们有着不同的含义和作用。
32 2
|
3月前
|
存储 C#
C# 一分钟浅谈:继承与多态性的实践
【9月更文挑战第2天】本文从基础入手,详细介绍了面向对象编程中继承与多态性的核心概念。通过 `Animal`、`Dog` 和 `Cat` 类的示例代码,展示了如何利用继承重用代码及多态性实现不同对象对同一方法的多样化响应,帮助读者更好地理解和应用这两个重要概念,提升面向对象编程能力。
48 3
|
6月前
|
C++
C++一分钟之-继承与多态概念
【6月更文挑战第21天】**C++的继承与多态概述:** - 继承允许类从基类复用代码,增强代码结构和重用性。 - 多态通过虚函数实现,使不同类对象能以同一类型处理。 - 关键点包括访问权限、构造/析构、菱形问题、虚函数与动态绑定。 - 示例代码展示如何创建派生类和调用虚函数。 - 注意构造函数初始化、空指针检查和避免切片问题。 - 应用这些概念能提升程序设计和维护效率。
47 2
|
7月前
|
存储 安全 编译器
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
62 0
|
7月前
|
存储 C++
C++类与对象【多态】
C++类与对象【多态】
|
编译器 C++
C++类和对象-继承&多态
C++类和对象-继承&多态
85 0
动态绑定,多态(带你从本质了解多态)(下)
动态绑定,多态(带你从本质了解多态)