​C++中虚函数 纯虚函数 虚基类的基础知识点​

news/2024/4/29 14:51:42/文章来源:https://blog.csdn.net/weixin_61813771/article/details/127729143

一、前言

     原文转载自 c++中的 虚函数 纯虚函数 虚基类_名字全都被占用了的博客-CSDN博客,为了理解下C++中虚函数 纯虚函数 虚基类的基础知识点。

二、虚函数 纯虚函数 虚基类三者区别      

    1.虚函数是用于多态中virtual修饰父类函数,确保父类指针调用子类对象时,运行子类函数的。

    2.纯虚函数是用来定义接口的,也就是基类中定义一个纯虚函数,基类不用实现,让子类来实现。

    3.虚基类是用来在多继承中,如果父类继承自同一个父类,就只实例化一个父类(说的有点绕,就是只实例化一个爷爷的意思)

2.1、虚函数

    这个和多态有关,多态的定义不清楚的话到其他地方先了解一下,多态的三个必要条件:1.继承 2.重载 3.父类指针指向子类对象。

看一下程序

第一个是没有使用virtual的,没有使用多态的:

 
  1. class A

  2. {

  3. public:

  4. void printf(){

  5. cout<<"printf A"<<endl;

  6. }

  7. };

  8. class B : public A

  9. {

  10. public:

  11. void printf(){

  12. cout<<"printf B"<<endl;

  13. }

  14. };

  15. int main(int argc, const char * argv[])

  16. {

  17. A *a = new A();

  18. a->printf();

  19. B *b = new B();

  20. b->printf();

  21. return 0;

  22. }

结果:

printf A

printf B

这是最基本的用法,没有多态,只有继承,下面是使用了多态但是没有引用virtual关键字的情况,多态的作用请参考其他地方的文章:

 
  1. int main(int argc, const char * argv[])

  2. {

  3. A *a = new B();

  4. a->printf();

  5. return 0;

  6. }

结果:

printf A

因为类的定义都一样,所以就没再写出来了,当父类指针指向子类对象的时候,如果不使用virtual,父类调用方法的时候还是调用了父类自己的方法,没有调用子类重写的方法,所以就没有实现到多态的作用,我们再来在父类中试试加入virtual关键字看看:

 
  1. class A

  2. {

  3. public:

  4. virtual void printf(){

  5. cout<<"printf A"<<endl;

  6. }

  7. };

  8. class B : public A

  9. {

  10. public:

  11. void printf(){

  12. cout<<"printf B"<<endl;

  13. }

  14. };

  15. int main(int argc, const char * argv[])

  16. {

  17. A *a = new B();

  18. a->printf();

  19. return 0;

  20. }

结果

printf B

     virtual是加入到父类中的,子类的代码没改变,main函数还是父类指针指向子类对象,结果终于可以打印到子类重写的方法了,所以证实了虚函数是用于多态中virtual修饰父类该重写的函数,确保父类指针调用子类对象时运行子类函数的。

2.2 、纯虚函数

    纯虚函数就像java的接口,使用了纯虚函数的类不能被实例化,定义了纯虚函数的类不用写纯虚函数的实现,由子类实现,下面看代码:

 
  1. class A

  2. {

  3. public:

  4. virtual void printf() =0;

  5. };

  6. void A::printf()//纯虚函数可以不写实现

  7. {

  8. cout<<"printf A"<<endl;

  9. }

  10. class B : public A

  11. {

  12. public:

  13. void printf(){

  14. cout<<"printf B"<<endl;

  15. }

  16. };

  17. int main(int argc, const char * argv[])

  18. {

  19. A *a =new A();//编译出错,纯虚函数的类不能实例化

  20. a->printf();

  21. return 0;

  22. }

virtual void printf() = 0;这是虚函数的写法,我在下面写了虚函数的实现 void A::printf(),其实写不写都没关系,写了也起不了作用= 。= 。然后我才main函数中尝试吧纯虚函数的类实例化,结果直接报错,说明纯虚函数是不能实例化的。

 
  1. int main(int argc, const char * argv[])

  2. {

  3. A *a =newB();//这里使用了多态

  4. a->printf();

  5. return 0;

  6. }

结果:

printf B

把main函数的a指向了子类的对象,结果可以正确打印出子类的方法。由此说明了纯虚函数也是为多态服务的,它的作用是定义一个接口,让子类去实现。

2.3、虚基类

虚基类是c++独有的东西,因为c++中有多继承,也是关键字virtual相关的定义。

先来说说多继承,如果爷爷类(暂把父类的父类暂定为爷爷类= = ),父类继承自爷爷类。如果孙类继承自多个父类(听起来有点怪异),那么如果不使用虚基类,就会实例化多个爷爷类对象(越说越离奇),编译器会报错,说有歧义性。如果父类继承自虚基类,则可以解决多个父类不会实例化多个爷爷的问题,就是只有一个爷爷。

 
  1. class Grandfather{

  2. public:

  3. int flag;

  4. Grandfather(){

  5. flag = 1;

  6. }

  7. };

  8. class Father1:publicGrandfather{

  9. public:

  10. Father1(){

  11. flag = 2;

  12. }

  13. };

  14. class Father2:publicGrandfather{

  15. public:

  16. Father2(){

  17. flag = 3;

  18. }

  19. };

  20. class Son:public Father1,publicFather2{

  21. };

  22. int main(int argc, const char * argv[])

  23. {

  24. Son *son = new Son();

  25. cout<<son->flag<<endl;//这里编译错误,没法指定flag是指定那一个,歧义

  26. return 0;

  27. }

如果没有使用虚基类,多个父类继承自同一个爷爷类,就会产生歧义,到底是不是同一个爷爷?如果父类继承虚基类就不同了:

 
  1. class Grandfather{

  2. public:

  3. int flag;

  4. Grandfather(){

  5. flag = 1;

  6. cout<<"Grandfather flag = "<<flag <<endl;

  7. }

  8. };

  9. class Father1:virtualpublicGrandfather{

  10. public:

  11. Father1(){

  12. flag = 2;

  13. cout<<"Father1 flag = "<<flag<<endl;

  14. }

  15. };

  16. class Father2:virtualpublicGrandfather{

  17. public:

  18. Father2(){

  19. flag = 3;

  20. cout<<"Father2 flag = "<<flag<<endl;

  21. }

  22. };

  23. class Son:public Father1,publicFather2{

  24. };

  25. int main(int argc, const char * argv[])

  26. {

  27. Son *son = new Son();

  28. cout<<son->flag<<endl;

  29. return 0;

  30. }

结果:

Grandfather flag = 1

Father1 flag = 2

Father2 flag = 3

3

现在,可以运行了,class Father2:virtual public Grandfather,就是继承虚基类的写法,爷爷对象只有一个,爷爷类的变量也只实例化了一次,那为什么最后打印出来的是3呢?看构造函数的顺序就可以看出来了,现在构造了爷爷类,再构造第一个继承的父类,最后继承第二个继承的父类,因此flag最后保持在第二个父类的修改值里了。

总的来说,虚函数 ,纯虚函数是为了多态服务,虚基类是为了只实例化一次基类存在的

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

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

相关文章

一、MongoDB简介

文章目录 一、MongoDB简介1、NoSQL简介2、什么是MongoDB ?3、MongoDB 特点4、安装mongodb5、MongoDB 概念解析5.1 数据库5.2 文档5.3 集合5.4 MongoDB 数据类型 6、适用场景 一、MongoDB简介 1、NoSQL简介 NoSQL(NoSQL Not Only SQL)&#xff0c;意即反SQL运动&#xff0c;…

密码学安全性证明(一)Cramer-Shoup密码系统

Cramer-Shoup密码系统来自于A Practical Public Key CryptosystemProvably Secure against Adaptive ChosenCiphertext Attack这篇论文 CDH问题回顾&#xff1a; 已知(g,g^x, gk)能否计算gxk DDH问题回顾&#xff1a; 已知(g,g^x, g^k &#xff0c;D)能否判断D是否等于g^xk 注意…

单片机GD32F303RCT6 (Macos环境)开发 (二十二)—— FreeRTOS的移植

GD32F303RCT6 FreeRTOS的移植 1、在Application的Makefile中增加freertos的编译选项&#xff0c;如下&#xff1a; ENABLE_FREERTOS yes ifeq ($(ENABLE_FREERTOS), yes) APPLICATION_FLAGS \ -DENABLE_FREERTOS endif2、Application目录下增加FreeRtos目录&#xff0c;将下…

【STC8】热启动串口指令下载

前言 在目标开发板没有装载自动下载电路的时候&#xff0c;往往需要冷启动&#xff0c;也就是需要手动开关电源&#xff0c;来达到单片机复位下载。当然还有一种方法是热启动&#xff0c;通过串口接收到自定义的指令后&#xff0c;软件执行复位下载。这就是本文介绍的内容。 材…

MIT6.824 lecture5上课笔记(涉及到Lab2A)- Go threads and raft

总结&#xff1a;本节课讲解了一些会在lab2中使用到的go的多线程技巧&#xff0c;会给一些简单的demo&#xff0c;lab2中可能会借鉴这些demo。 详细的Lab2 raft算法实现源码&#xff0c;请参考我的个人仓库&#xff08;记得点颗星星&#xff09;, 配合readme食用更佳。 MIT6.…

【Linux升级之路】3_Linux进程概念

&#x1f31f;hello&#xff0c;各位读者大大们你们好呀&#x1f31f; &#x1f36d;&#x1f36d;系列专栏&#xff1a;【Linux升级之路】 ✒️✒️本篇内容&#xff1a;认识冯诺依曼系统&#xff0c;操作系统概念与定位&#xff0c;深入理解进程概念&#xff08;了解PCB&…

推荐一款C端的低代码产品

目录 写在前面 低代码平台 平台怎么选 各平台区别 为什么选它 写在前面 大家都知道低代码这个叫法是从B端叫起来的&#xff0c;也就是说不管是业务人员还是开发人员&#xff0c;都是企业内部使用。那么有没有C端的&#xff0c;且免费使用的低代码产品呢&#xff1f; 低代码…

若依源码解析:DataScopeAspect实现数据范围的控制

文章目录 源代码使用场景界面操作SysDeptServiceImplSysUserServiceImplSysUserMapperDataScope定义 代码解析Aspect和Component不同的数据权限类型Before通知处理数据范围的方法 源代码 Aspect Component public class DataScopeAspect {/*** 全部数据权限*/public static fi…

springboot旅游资源管理系统门票酒店预订系统_b0a6b

Spring Boot 是 Spring 家族中的一个全新的框架&#xff0c;它用来简化Spring应用程序的创建和开发过程。也可以说 Spring Boot 能简化我们之前采用SSM&#xff08;Spring MVC Spring MyBatis &#xff09;框架进行开发的过程。config&#xff1a;主要用来存储配置文件&#…

【学习版】WeDataSphere组件容器化部署实战

WeDataSphere组件容器化部署文档 WeDataSphere是微众银行的大数据开发平台。目前为让用户能够快速体验WeDataSphere所有组件&#xff0c;我们提供一个使用Docker构建的镜像包&#xff0c;里面包括基础组件Hadoop, Spark, Hive, Flink, MySQL。WeDataSphere的组件有DSS&#xff…

【动态规划专栏】--基础-- 动态规划经典题型

目录 动态规划 动态规划思维&#xff08;基础&#xff09; 状态表示&#xff08;最重要&#xff09; 状态转移方程&#xff08;最难&#xff09; 初始化&#xff08;细节&#xff09; 填表顺序&#xff08;细节&#xff09; 返回值&#xff08;结果&#xff09; 1、第 …

[山海关crypto 训练营 day17]

[HNCTF 2022 WEEK3]pnearq 题目代码和数据 from Crypto.Util.number import * from gmpy2 import next_prime from flag import flagp getPrime(1024) q next_prime(p) n p*q e 0x10001 c pow(bytes_to_long(flag), e, n) print(f"n {n}") print(f"c {…

代理、正向代理与反向代理

一、代理 1.1 什么是代理 代理也被叫做网络代理&#xff0c;是一种比较特殊的网络服务&#xff0c;允许一个终端&#xff08;通常指客户端&#xff09;通过这个服务与另一个终端&#xff08;通常指服务器端&#xff09;进行非直接的连接。例如&#xff1a;一些网关、路由器等…

队列的实现(附含两道经典例题)

&#x1f349;文章主页&#xff1a;阿博历练记 &#x1f4d6;文章专栏&#xff1a;数据结构与算法 &#x1f68d;代码仓库&#xff1a;阿博编程日记 &#x1f365;欢迎关注&#xff1a;欢迎友友们点赞收藏关注哦&#x1f339; 文章目录 &#x1f33e;前言&#x1f3ac;队列&…

[web安全原理分析]-XXE漏洞入门

前言 XXE漏洞 XXE漏洞全称(XML External Entity Injection)即xml外部实体注入漏洞&#xff0c;XXE漏洞发生在应用程序解析XML输入时&#xff0c;没有禁止外部实体的加载&#xff0c;导致可加载恶意外部文件&#xff0c;造成文件读取、命令执行、内网端口扫描、攻击内网网站、…

国内IEEEXplore访问加速(adblock)

访问IEEEXplore&#xff0c;需要加载一个网页需要等好久&#xff0c;然后才出来&#xff0c;就很奇怪&#xff0c;查到网上有同样这个问题 感谢原博主&#xff1a;ieeexplore加载慢的解决办法 核心原因&#xff1a; 国内无法访问地址&#xff1a; https://cdn.jwplayer.com/…

Redis数据结构——QuickList、SkipList、RedisObjective

承接上文&#xff0c;本文主要介绍QuickList、SkipList、RedisObjective 四、 Redis数据结构-QuickList 问题1&#xff1a;ZipList虽然节省内存&#xff0c;但申请内存必须是连续空间&#xff0c;如果内存占用较多&#xff0c;申请内存效率很低。怎么办&#xff1f; ​ 答&a…

C++入门篇---(命名空间、缺省参数、以及输入、输出)

前言 c 我来了,恭喜牛牛解锁新世界.开启c的学习之旅. &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f;C语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:讲解C…

pg事务:子事务

什么是子事务&#xff1f; 一般事务只能整体提交或回滚&#xff0c;而子事务允许部分事务回滚。 SAVEPOINT p1 在事务里面打上保存点标记。不能直接提交子事务&#xff0c;子事务也是通过事务的提交而提交。不过可以通过ROLLBACK TO SAVEPOINT p1回滚到该保存点。 子事务在大…

【场景方案】如何去设计并二次封装一个好用的axios,给你提供一个好的参考(1.0版)

文章目录 前言文件结构建议封装的文件结构接口管理文件 二次封装axios的初始配置initutilswebConfig 再封一层环境配置使用 前言 以下演示基于vue3与element-plus 文件结构建议 封装的文件结构 把二次封装axios所有有关的代码全部放在request文件夹中&#xff0c;其中init.js…