C++ 多态

文章目录

一:概念

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

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

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

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

二、定义和实现

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

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

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

注意一下

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

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

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

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

三、析构函数的重写

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

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

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

四、 override和final关键字

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

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

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

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

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

重载:

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

重写:

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

隐藏:

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

六、纯虚函数和抽象类

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

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

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

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

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

    七、多态的原理

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

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

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


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

5.1 虚函数表

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

答案是12字节

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

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

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

动态绑定和静态绑定

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

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


相关推荐
贾斯汀玛尔斯4 小时前
每天学一个算法--LSM-Tree(Log-Structured Merge Tree)
java·算法·lsm-tree
bitt TRES4 小时前
springboot与springcloud对应版本
java·spring boot·spring cloud
Y001112365 小时前
JavaWeb-end
java·servlet·web
bzmK1DTbd5 小时前
Git版本控制:Java项目中的分支管理与合并策略
java·开发语言·git
许长安5 小时前
RPC 同步调用基本使用方法:基于官方 RouteGuide 示例
c++·经验分享·笔记·rpc
Rust研习社5 小时前
为什么 Rust 没有空指针?
开发语言·后端·rust
kyriewen116 小时前
WebAssembly:前端界的“外挂”,让C++代码在浏览器里跑起来
开发语言·前端·javascript·c++·单元测试·ecmascript
JWASX7 小时前
【RocketMQ 生产者和消费者】- 事务源码分析(1)
java·rocketmq·java-rocketmq
其实防守也摸鱼8 小时前
CTF密码学综合教学指南--第九章
开发语言·网络·python·安全·网络安全·密码学·ctf
砚底藏山河8 小时前
Python量化开发:2026最佳实时股票数据API接口推荐与对比
开发语言·windows·python