【QandA C++】面向过程、面向对象、多态的原理、虚函数表、虚表指针、虚析构、虚构造、虚函数、纯虚函数等重点知识汇总

目录

面向过程和面向对象

面向对象的三大特性

多态的条件和原理

虚表存放位置、虚表指针初始化时间

析构函数为什么要为虚函数

构造函数为什么不能为虚函数

虚函数和纯虚函数的实现原理

虚函数和纯虚函数的区别


面向过程和面向对象

面向过程的,关注的是过程,分析出求解问题的步骤,通过函数调用逐步解决问题。

面向过程强调将程序分解为一系列的过程(函数、过程等),这些过程按照一定的顺序执行,最终完成整个程序的功能。

面向对象(OOP)的,关注的是对象,将一件事拆分成不同的对象,靠对象之间的交互完成。

在面向对象中,程序员通常不需要手动管理程序中的数据和控制流程,而是通过封装、继承、多态等方式来实现代码复用和模块化。

面向对象有三大特性

我们就外卖系统来看看面向过程和面向对象之间的区别:

  • 面向过程,我们的关注点应该是用户下单、骑手接单以及骑手送餐这三个过程。
  • 面向对象,那我们的关注点应该就是客户、商家以及骑手这三个类对象之间的关系。

面向过程更注重步骤和操作,而面向对象更注重将问题抽象为对象,并通过对象之间的交互来解决问题。

让我们考虑一个生活中的例子来说明面向过程和面向对象的区别。

假设你要制作一杯咖啡。

  1. 面向过程:在面向过程的方式下,你可能会按照一系列的步骤来制作咖啡。你需要准备杯子、煮沸水、研磨咖啡豆、将咖啡粉加入过滤器、倒入热水、搅拌、加入牛奶和糖等。整个过程是基于一系列的步骤和操作。
  2. 面向对象:在面向对象的方式下,你可以将咖啡制作过程中的对象抽象为类。你可以有一个咖啡机类,它有一个制作咖啡的方法。该方法内部包含了煮沸水、研磨咖啡豆、加入过滤器等操作。你还可以有一个杯子类,它有一个倒入咖啡的方法。你可以创建一个咖啡机对象和一个杯子对象,然后通过调用方法来制作咖啡。

面向对象的三大特性

封装:封装是将数据和方法封装在一个单元中,以便控制访问和保护数据的完整性。封装隐藏了实现细节,只暴露必要的接口供外部使用。

例子:考虑一个电视机。电视机封装了内部的电子元件和电路板,对外部用户只提供了开关、音量调节、频道切换等操作的接口。用户无需知道电视机内部的工作原理,只需要通过提供的接口来使用电视机。

继承:继承是一种机制,允许从现有类派生出新类,并继承父类的属性和方法。通过继承,子类可以重用父类的代码,扩展父类的功能,并添加自己的特定行为。

例子:考虑一个动物类的继承关系。有一个父类Animal,它定义了通用的属性和方法,例如名称、年龄和移动方式等。然后可以创建子类,如Dog、Cat和Bird,这些子类继承了Animal类的属性和方法,并可以添加自己特定的属性和方法,如Dog类可以有狗叫的方法,Cat类可以有抓老鼠的方法。

多态多态是指同一种操作或方法在不同的对象上有不同的行为。多态性将父类类型的变量指向子类对象,并且根据实际对象的类型,在运行时选择合适的方法来执行。多态允许使用一个统一的接口来处理不同的对象类型。即同一段代码可以根据对象的不同而执行不同的操作。多态提高了代码的灵活性和可扩展性。

抢红包,大家都有点击这个动作,但是有人会获得红包,有人却没有,这就有了不同的行为!

多态的条件和原理

虚函数是C++中用于实现多态的机制。

构成多态的条件:

多态构成的两个条件

  1. 一是完成虚函数的重写,必须完成虚函数的重写是因为我们需要完成子类虚表当中虚函数地址的覆盖
  2. 二是必须使用父类的指针或者引用去调用虚函数。

多态的原理:

  1. 首先,定义一个父类,其中包含一个或多个虚函数。
  2. 子类重写虚函数
  3. 当通过基类的指针或引用调用虚函数时,实际上是通过虚表指针来找到虚函数地址,然后调用相应的函数。
  4. 虚表指针在对象的内存布局中作为一个隐藏的成员存在,并且对于每个对象只有一个虚表指针。
  5. 虚表指针指向类的虚函数表,根据对象的实际类型来查找相应的虚函数地址,实现了运行时的多态性和动态绑定。
  1. 虚函数的主要作用是实现多态的机制. 关于多态, 简单地说就是用父类型的指针指向其子类对象, 然后通过父类的指针调用实际子类的成员函数, 这种技术可以让父类的指针有多种形态. 如果调用非虚函数, 则无论实际对象是什么类型的, 都执行父类类型所定义的函数. 非虚函数在编译时根据调用该函数的对象的类型而确定. 如果调用虚函数, 则直到运行时才能确定调用哪个函数.
  2. 虚函数的作用是动态联编, 也就是在程序的运行阶段动态的选择合适的成员函数, 在定义了虚函数后, 可以在子类中对虚函数重新定义. 如果在子类中没有对虚函数重新定义, 则它继承其父类的虚函数.
  3. 虚函数是通过一张虚函数表来调用的; 实际上虚表当中存储的就是虚函数的地址, 子类虽然继承了父类的虚函数, 但是子类对父类的虚函数进行了重写; 因此, 子类对象的虚表当中存储的是父类的虚函数的地址和重写的虚函数的地址. 这就是为什么虚函数的重写也叫做覆盖, 覆盖就是指虚表中虚函数地址的覆盖, 重写是语法的叫法, 覆盖是原理层的叫法.

虚表存放位置、虚表指针初始化时间

  1. 虚函数表(vtable)是在构造函数初始化列表阶段初始化的, 存放在数据段
  2. 虚函数则位于代码段
  3. 对于有虚函数的类,对该类进行实例化时,在构造函数执行时会对虚表指针进行初始化,并且存在对象内存布局的最前面。
  4. 虚表指针(vptr)的位置取决于对象在哪.

析构函数为什么要为虚函数

  1. 如果父类的析构函数为虚函数, 此时子类析构函数只要定义, 无论是否加 virtual, 都与父类的析构函数构成重写.
  2. 虚析构可以保证当我们 new 一个子类, 然后用父类的指针指向这个子类对象, 释放父类指针时可以释放掉子类的空间, 可以防止内存泄漏!
  3. 析构函数如果没有虚函数的动态绑定功能, 就只根据指针的类型来进行调用, 而不是指针绑定的对象来进行调用, 所以没有虚函数的析构函数只是调用父类的析构函数

那父类和子类的析构函数构成重写的意义何在呢?

试想以下场景:分别new一个父类对象和子类对象,此时没有将析构函数设置为虚函数. 并均用父类指针指向它们,然后分别用delete调用析构函数并释放对象空间。

//分别new一个父类对象和子类对象,并均用父类指针指向它们
Person* p1 = new Person;
Person* p2 = new Student;//使用delete调用析构函数并释放对象空间
delete p1;
delete p2;

在这种场景下就会导致程序崩溃, delete p1和delete p2 调用的都是父类的析构函数, 而我们所期望的是 p1 调用父类的析构函数, p2 调用的是子类的析构函数, 我们期望的是一种多态行为.

只有父类和子类的析构函数构成了重写, 才能使得 delete 按照我们的预期进行析构函数的调用. 期望一种多态的行为!

构造函数为什么不能为虚函数

1、这是因为虚函数的调用需要一个已经构造完毕的对象,而构造函数本身就是用于创建对象的初始函数。虚函数表和虚函数指针在构造函数阶段还没有建立,因此无法进行虚函数的调用。

2、从存储空间上看, 虚函数表指针是存储在对象的内存空间上的. 如果将构造函数设置为虚函数, 就需要通过虚表来调用, 但是对象还没有实例化, 也就是内存空间还没有, 内存还没有怎么拿到虚表指针呢, 没有虚表指针怎么找到虚函数表呢? 所以构造函数不能是虚函数! (相互矛盾了)

3、虚表是在构造函数初始化列表阶段完成初始化的, 存储在数据段. 所以构造函数不可能成为虚函数

虚函数和纯虚函数的实现原理

虚函数的实现原理:

  1. 虚函数的主要作用是实现多态的机制. 关于多态, 简单地说就是用父类型的指针指向其子类对象, 然后通过父类的指针调用实际子类的成员函数, 这种技术可以让父类的指针有多种形态. 如果调用非虚函数, 则无论实际对象是什么类型的, 都执行父类类型所定义的函数. 非虚函数在编译时根据调用该函数的对象的类型而确定. 如果调用虚函数, 则直到运行时才能确定调用哪个函数.
  2. 虚函数的作用是动态联编, 也就是在程序的运行阶段动态的选择合适的成员函数, 在定义了虚函数后, 可以在子类中对虚函数重新定义. 如果在子类中没有对虚函数重新定义, 则它继承其父类的虚函数.
  3. 虚函数是通过一张虚函数表来调用的; 实际上虚表当中存储的就是虚函数的地址, 子类虽然继承了父类的虚函数, 但是子类对父类的虚函数进行了重写; 因此, 子类对象的虚表当中存储的是父类的虚函数的地址和重写的虚函数的地址. 这就是为什么虚函数的重写也叫做覆盖, 覆盖就是指虚表中虚函数地址的覆盖, 重写是语法的叫法, 覆盖是原理层的叫法.

纯虚函数的实现原理:

  1. 纯虚函数是在父类中声明的函数, 它在父类中没有定义. 但要求任何子类都要定义自己实现方法
  2. 在父类中实现纯虚函数的方法是在虚函数原型后加上"=0"
  3. 在很多情况下, 父类本身生成对象是不合理的, 例如: 动物作为一个父类可以派生出老虎, 狮子等子类, 但动物本身生成对象明显不合常理. 为了解决这个问题, 就要将函数定义为纯虚函数, 编译器要求在子类中必须予以重写以实现多态性
  4. 含有纯虚函数的类称为抽象类, 它不能生成对象. 必须在子类中也重新声明该函数(不要带=0), 否则该子类也不能实例化
  5. 纯虚函数的意义在于让所有的子类对象都可以执行纯虚函数的操作.
  6. 纯虚函数在类的虚表中对应的表项被赋值为0, 也就是指向了一个不存在的函数. 由于编译器绝对不允许有调用一个不存在的函数的可能, 所以该类不能生成对象. 在它的子类中, 除非重写此函数, 否则也不能生成对象.

虚函数和纯虚函数的区别

  1. 虚函数和纯虚函数可以定义在同一个类中, 含有纯虚函数的类被称为抽象类, 而只含有虚函数的类不能被称为抽象类
  2. 纯虚函数只有声明没有实现, 虚函数既有声明也有实现
  3. 虚函数的定义形式为: virtual flying(){ }; 纯虚函数的定义形式为: virtual flying() = 0 { };
  4. 虚函数可以有默认的实现,如果派生类没有对虚函数进行重写,将会使用基类中的默认实现。
  5. 纯虚函数没有默认的实现,派生类必须对纯虚函数进行重写并提供具体的实现。
  6. 含纯虚函数的类被称为抽象类,抽象类无法被实例化,只能用作基类。派生类必须实现抽象类中的所有纯虚函数,才能成为具体类。
  7. 总结来说,虚函数允许在基类和派生类之间实现多态性,而纯虚函数则更加强调接口的定义和派生类的实现。虚函数可以有默认实现,而纯虚函数必须在派生类中进行重写。纯虚函数的存在使得基类成为抽象类,不能被实例化,只能用作派生类的基类。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/86387.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

Kubernetes的容器批量调度引擎 Volcano

一个用于高性能工作负载场景下基于Kubernetes的容器批量调度引擎 Volcano是在Kubernetes上运行高性能工作负载的容器批量计算引擎。 它提供了Kubernetes目前缺少的一套机制,这些机制通常是许多高性能 工作负载所必需的,包括: - 机器学习/深度…

flurl监听报错返回的信息

前言 开发过程中使用flurl.http发送http请求,但是服务器有时候会返回400、404这样的错误,有时候会携带信息,比如json提示或者xml或者html的提示,一开始不知道怎么捕捉,需要结合apipost调试,经过百度查找&a…

关于遍历,递归,Divide and Conque,回溯,Memorization和DP的一个小结

个人的一个小结,参考了一些网上的思路: 遍历和Divide and Conquer分别代表至上而下和至下而上的两种处理问题的思路 遍历: 至上而下,对应回溯算法。函数签名一般是没有返回值的,例如void backtrack(…) Divide and Conquer: 至下…

三.vue2路由知识全总结

Vue Devtools:插件安装,展示模块中的数据 vue-router 应用场景:Vue Router 是 Vue.js 的官方路由。它与 Vue.js 核心深度集成,让用 Vue.js 构建单页应用变得轻而易举。 嵌套的路由/视图表模块化的、基于组件的路由配置路由参数、…

一拖三快充线(USB-C转三充)的解决方案--LDR6020P

DR6020P 是带有 3 组 6 路 DRP USB-C 及 PD 通信协议处理模块和 USB2.0 Device 功能的 16 位 RISC MCU,内置 8K16 位 MTP 程序存储器(可烧录 1000 次),512 字节的数据存储器(SRAM)。内置 LDO 5V 输出&#…

通讯网关软件011——利用CommGate X2ODBC实现DDE数据转入ODBC

本文介绍利用CommGate X2ODBC实将DDE数据源中的数据转入到ODBC数据源。CommGate X2ODBC是宁波科安网信开发的网关软件,软件可以登录到网信智汇(http://wangxinzhihui.com)下载。 【案例】如下图所示,将DDE数据源(如Excel)的数据写…

postgresql-触发器

postgresql-触发器 触发器概述创建触发器管理触发器删除触发器事件触发器创建事件触发器修改触发器删除事件触发器 触发器概述 PostgreSQL 触发器(trigger)是一种特殊的函数,当某个数据变更事件(INSERT、UPDATE、 DELETE 或者 TR…

uniapp获取openid

要获取用户的openid,需要使用微信小程序的登录API。以下是一个简单的示例代码: // 在page中引入wx-login组件 import wxLogin from /components/wx-loginexport default {components: { wxLogin },data() {return {openid: }},methods: {// wxLogin组件…

【3dmax】怎么将点删除而面保留

在编辑多边形模式下,选择点模式,选择要删除的点,在下拉面板中找到【移除】

Mysql——压缩包方式安装教程

一.Mysql压缩包下载方式 zip版(5.7及8.0)的下载需到官方网站下载,不同版本对应能安装在不同的操作系统下,本次介绍的是mysql-8.0.30-winx64在win10下的安装方式。 下载网址:MySQL :: Download MySQL Community Server …

【Nuxt3】路由参数(动态路由、嵌套路由)

页面路由参数 query 参数 目录 |- pages | |- foo.vue路由地址 https://loaclhost:8080/foo?id1参数获取 // foo.vue <template><div>id: {{ route.query.id }}</div> </template><script setup lang"ts"> const route useRoute() …

描述符——配置描述符

描述符定义 描述符实现 /*** brief USB configuration descriptor.*/ typedef struct __attribute__ ((packed)) {uint8_t bLength ; /**< Size of this descriptor in bytes. */uint8_t bDescriptorType ; /**< CONFIGURATION Descriptor Type. */ui…

基于同名面片的TLS测站点云配准

1、原理介绍 2、代码介绍 基于C++编写的程序代码如下,其依赖eigen矩阵运算库,在创建工程时包含库目录中使用了相对路径,因此其下载下来直接可以运行,不用单独在设置环境,非常方便。

<图像处理> Shi-Tomasi角点检测

Shi-Tomasi角点检测 参考Harris角点检测方法&#xff0c;它是通过下式来判断是否为角点的&#xff1a; R d e t ( C ( x , y ) ) − k ( t r a c e ( C ( x , y ) ) ) 2 Rdet(C(x,y))-k(trace(C(x,y)))^2 Rdet(C(x,y))−k(trace(C(x,y)))2 式中&#xff0c;C(x,y)为自相关矩…

云原生微服务治理:服务发现、负载均衡与熔断策略

文章目录 什么是云原生微服务治理&#xff1f;服务发现客户端发现服务器端发现 负载均衡Ribbon - 基于客户端的负载均衡Nginx - 基于服务器的负载均衡 熔断策略Hystrix - 熔断器模式 结论 &#x1f389;欢迎来到云计算技术应用专栏~云原生微服务治理&#xff1a;服务发现、负载…

Spring Cloud Gateway快速入门(二)——断言工厂

文章目录 前言1. 什么是Gateway断言工厂2. 为什么要使用断言2.1. 调试和开发&#xff1a;2.2. 防御性编程&#xff1a;2.3. 文档和可读性&#xff1a;2.4. 测试&#xff1a; 3. 常用的Gateway断言工厂3.1 Path断言工厂3.2 Method断言工厂3.3 Header断言工厂3.4 时间断言工厂 4.…

北邮22级信通院数电:Verilog-FPGA(1)实验一“跑通第一个例程” 过程中遇到的常见问题与解决方案汇总(持续更新中)

北邮22信通一枚~ 跟随课程进度更新北邮信通院数字系统设计的笔记、代码和文章 持续关注作者 迎接数电实验学习~ 获取更多文章&#xff0c;请访问专栏&#xff1a; 北邮22级信通院数电实验_青山如墨雨如画的博客-CSDN博客 目录 问题一&#xff1a;Verilog代码没有跑通 报…

LabVIEW使用ModbusTCP协议构建分布式测量系统

LabVIEW使用ModbusTCP协议构建分布式测量系统 分布式测量系统主要用于监控远程物体。这种系统允许对系统用户获得的数据进行全面的数据收集、处理、存储和组织访问。它们可能包括许多不同类型的传感器。 在任何具有互联网接入的个人计算机上运行的软件都会发送来自传感器的测…

【kubernetes】k8s各组件运行流程以及高可用

1 Master节点的组件 Master节点包含三个组件和存储组件(Master的组件不一定要跟存储组件部署在同一台机器)&#xff1a; apiserver&#xff1a;提供RESTful风格的操作的API&#xff0c;其它组件之间通过API进行交互scheduler&#xff1a;调度器&#xff0c;负责决策将Pod安排…

SpringCloud Alibaba - Sentinel

接上文SpringCloud Alibaba - Nacos 1.Sentinel 流量防卫兵 1.1 安装与部署 和Nacos一样&#xff0c;它是独立安装和部署的&#xff0c;下载地址https://github.com/alibaba/Sentinel/releases 下载后的jar放到目录 然后配置 启动并访问,用户名密码都是 sentinel 此时就…