C++ 多态

文章目录

一:概念

编译时多态(静态) 运行时多态(动态)

主要动态时多态:达到多种形态

给函数传不同对象完成不同的行为,达到不同效果

多态就是基类和派生类有共同的虚函数,满足三同,想实现一个功能

二、定义和实现

在类成员函数前加virtual修饰,形成虚函数

多态的实现需要满足两个条件:

  • 这个功能参数必须是基类的指针或者引用
  • 派生类必须堆基类的虚函数完成重写或者覆盖(返回值,函数名,参数(只看类型)都相同,函数体可以不相同)重写就是重写一下这个函数体,重写虚函数的实现

注意一下

  • 派生类的virtrul可以去掉,也可以构成多态,但是不规范,选择题会埋坑
  • 虚函数在类里声明加virtual关键字,在类外定义实现不需要加virtual
  • 友元函数不属于成员函数,不能实现为虚函数
  • 静态成员函数不能设置为虚函数,因为静态成员函数属于整个类,不属于具体的某个对象,调用时直接类::静态函数(),静态函数无需拿到this指针,就掉不到虚函数表,进而无法被写成虚函数,无法被重写

运行时跟基类没有关系,而是传谁(派生类)调用谁

只有在运行时,才知道基类的指针或者引用具体指向那个类的对象,进而再去找虚函数表,调用对应的虚函数,实现多态

你用不同的类去继承它,去重写这个虚函数。就可以调用同一个函数(函数体不同)去完成不同的功能,形成不同的形态

三、析构函数的重写

基类和派生类析构函数表面上不满足三同,因为函数名不同,但由于编译时析构函数同意编译成destroctor(),所以析构函数也可以构成重写;

为什么基类的中的析构函数建议设计成虚函数?

  • 如果你不设计成虚函数,如果一个基类的指针指向一个派生类,而这个派生类里面又有资源的申请,当你去调用析构函数时候,就默认调用基类析构,会有内存泄漏的风险;
  • 简单来说:父类的析构函数强力建议设置为虚函数,这样动态释放父类指针所指的子类对象时,能够达到析构的多态
  • 所以设计成虚函数构成重写,指向谁调用谁,就不会出现上述情况;且析构遵循先子后父,析构完派生类会自动调用基类析构函数;

四、 override和final关键字

提示:这里可以添加技术细节
override 在编译的时候检查时候检查是否构成多态,不构成编译报错

在函数参数列表的后面加上override就可以

  • 跟assert很像,区别是assert是运行时报错;
    final修饰的虚函数不能被重写;

五、重载/重写/隐藏的对比

相同函数名之间函数的关系

重载:

  • 首先构成重载的函数必须要在同一作用域
  • 两个函数函数名相同,参数类型和个数可以不同

重写:

  • 是在继承和多态之中出现的概念,两个函数的函数名,参数和返回值三者完全相同,函数体可以不同;调用时需要使用父类的指针或者引用进而构成重写

隐藏:

  • 当派生类中的函数名和父类中的函数名重复了,派生类就会隐藏父类的函数,不会去调用父类的重名函数
  • 可以说两个函数如果不构成重写就是隐藏

六、纯虚函数和抽象类

纯虚函数就是虚函数后面加上=0;

抽象类 特点:不能实例化出对象

实际应用中这个基类(抽象类)就会使用他的指针或者引用来指向派生类;

  • 派生类如果不重写纯虚函数,那么这个派生类也是抽象类

  • 所以在一定程度上如果基类是抽象类,派生类就会强制被要求重写虚函数

    七、多态的原理

    一个类里有虚函数,他的大小包含了这个类的成员变量和虚函数表---函数指针数组(首先他是一个数组,其次这个数组里面存放指针,而指针又指向一个个的虚函数,所以叫函数指针数组)

    也就是这个ptr他可能是Person本身,也可能是切出来的Person对象;

多态函数的调用,就是你传什么对象,他就去调用那个对象的虚表,找到对应的续表函数;


动态绑定(运行时确定地址)和静态绑定(编译时确定地址)

5.1 虚函数表

·来看一段代码,了解一下虚函数表

答案是12字节

原因就是有虚函数的类,他的大小都会有一个指向虚函数表的一个指针_vptr和他的成员变量构成

关于虚函数表(简称虚表)

  • 同一个类型的不同对象共享一个虚函数表
  • 派生类和基类的虚函数表不是一个,只是继承下来,地址也不一样
  • 一个类的不同对象共用一张虚表
    虚函数和普通函数一样存在于代码段
    虚函数表存在常量区

动态绑定和静态绑定

静态绑定就是编译时就只知道要调用哪个函数,对不满⾜多态条件(指针或者引⽤+调⽤虚函数)的函数就是静态绑定

动态绑定就是运行时才知道要调用那个函数,多态函数的调用就是动态绑定,运行时候才去虚表里找要调用的函数


相关推荐
Devin~Y2 小时前
大厂Java面试实战:Spring Boot/WebFlux、Redis+Kafka、K8s可观测性与Spring AI RAG/Agent三轮连环问
java·spring boot·redis·kafka·kubernetes·resilience4j·spring webflux
@atweiwei2 小时前
LangChainRust:用 Rust 构建高性能 LLM 应用的完整指南
开发语言·人工智能·ai·rust·大模型·llm·agent
lclin_20202 小时前
大恒Galaxy SDK MFC开发(1):枚举、打开、开始_停止采集基础流程(无画面版)
c++·机器视觉·工业相机·大恒相机·galaxysdk
老约家的可汗2 小时前
搜索二叉树的概念及使用
java·开发语言
初圣魔门首席弟子2 小时前
bug20260415
c++·bug
不知名的老吴2 小时前
R语言4.3.0安装包百度网盘中文版下载与详细安装指南
开发语言·r语言
棉猴2 小时前
python海龟绘图之计算夹角towards()
开发语言·python·turtle·海龟绘图·towards
张人玉2 小时前
SMT 贴片机上位机项目
开发语言·c#
我不是懒洋洋2 小时前
【经典题目】链表OJ(相交链表、环形链表、环形链表II、随机链表的复制)
c语言·开发语言·数据结构·链表·ecmascript·visual studio