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

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

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

相关文章
|
5月前
|
自然语言处理 API 数据安全/隐私保护
手把手教你搭建 cssbuy 淘宝代购系统
随着全球电商的兴起,淘宝成为海外用户青睐的购物平台,但语言、支付和物流等问题限制了其直接使用。CSSBuy 等淘宝代购系统应运而生,为海外用户提供便捷的购物体验。本文详细解析如何搭建类似系统,涵盖需求分析与功能模块设计。目标用户包括海外华人、留学生及外国消费者,核心功能涉及商品搜索、代购下单、支付集成、物流管理、客服售后及多语言支持等。系统模块包括用户管理、商品管理、购物车、订单管理、支付管理、物流管理、客服售后和多语言模块,全面满足海外用户的购物需求。
|
10月前
|
传感器 监控 物联网
物联网与虚拟现实:未来科技的发展趋势与应用探索####
本文探讨了物联网(IoT)与虚拟现实(VR)这两大新兴技术的最新发展趋势及其广泛的应用场景。通过分析这些技术的核心原理、当前发展现状以及未来的潜在影响,揭示了它们如何独立演进又相互融合,共同推动社会进步。本文旨在为读者提供一个全面的了解,以把握未来科技的脉络,迎接技术革新带来的挑战与机遇。 ####
|
8月前
|
Linux iOS开发 MacOS
deepseek部署的详细步骤和方法,基于Ollama获取顶级推理能力!
DeepSeek基于Ollama部署教程,助你免费获取顶级推理能力。首先访问ollama.com下载并安装适用于macOS、Linux或Windows的Ollama版本。运行Ollama后,在官网搜索“deepseek”,选择适合你电脑配置的模型大小(如1.5b、7b等)。通过终端命令(如ollama run deepseek-r1:1.5b)启动模型,等待下载完成即可开始使用。退出模型时输入/bye。详细步骤如下图所示,轻松打造你的最强大脑。
14365 86
|
9月前
|
人工智能 自然语言处理 安全
Poe AI国内能用吗?回答是:能用!记住这个使用方法就够了!
国内用户如何畅玩 Poe AI?告别网络限制,开启AI创作之旅!
4296 15
|
11月前
|
安全 API 数据安全/隐私保护
史上最全最完整,最详细,软件保护技术-程序脱壳篇-逆向工程学习记录(一)
欢迎访问我的原站!本文详细介绍了程序脱壳技术,包括壳的定义、作用、执行过程、OEP(原始入口点)的概念及查找方法。文章通过多个实例,逐步演示了如何使用OD(OllyDbg)等工具进行脱壳操作,涵盖了压缩壳、加密壳等多种类型的壳。内容详尽,适合逆向工程初学者深入学习。[点击查看原文](https://www.oisec.cn/index.php/archives/520/)
952 0
|
Ubuntu 安全 网络协议
|
SQL 算法 Java
MyBatis-Plus详解(3)
MyBatis-Plus详解(3)
219 0
|
关系型数据库 MySQL
【MySQL进阶之路 | 基础篇】约束之CHECK约束与DEFAULT约束
【MySQL进阶之路 | 基础篇】约束之CHECK约束与DEFAULT约束
instanceof是什么~
instanceof是什么~
144 0
|
存储 算法 Linux
[GUET-CTF2019]encrypt 题解
[GUET-CTF2019]encrypt 题解
300 0