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

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

在上一章节中,我们讲述了虚函数和虚函数表,我们知道了不管在类中有多少个虚函数,都不会使类的大小扩大,在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++虚函数、继承和多态详解
61 0
|
2月前
多态和动态绑定的区别是什么?
【10月更文挑战第14天】多态和动态绑定是面向对象编程中两个重要的概念,但它们有着不同的含义和作用。
28 2
|
6月前
|
C++
C++一分钟之-继承与多态概念
【6月更文挑战第21天】**C++的继承与多态概述:** - 继承允许类从基类复用代码,增强代码结构和重用性。 - 多态通过虚函数实现,使不同类对象能以同一类型处理。 - 关键点包括访问权限、构造/析构、菱形问题、虚函数与动态绑定。 - 示例代码展示如何创建派生类和调用虚函数。 - 注意构造函数初始化、空指针检查和避免切片问题。 - 应用这些概念能提升程序设计和维护效率。
44 2
|
7月前
|
存储 安全 编译器
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
【C/C++ 多态核心 20240115更新】C++虚函数表:让多态成为可能的关键
55 0
|
7月前
|
存储 C++
C++类与对象【多态】
C++类与对象【多态】
|
7月前
|
NoSQL 编译器 程序员
『 C++类与对象 』虚函数与多态
『 C++类与对象 』虚函数与多态
再次理解多态概念
再次理解多态概念
66 0
|
编译器 C++
C++类和对象-继承&多态
C++类和对象-继承&多态
84 0
动态绑定,多态(带你从本质了解多态)(下)
动态绑定,多态(带你从本质了解多态)