[C++]面向对象语言三大特性--多态
Posted 一个正直的男孩
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了[C++]面向对象语言三大特性--多态相关的知识,希望对你有一定的参考价值。
大家这篇文章描述的是我在学习多态时的一些学习笔记,和问题,还有一些面试题,希望这篇文章对你会有帮助
文章目录
- 什么是多态?
- 如何才可以看到多态这一属性呢?(概念)
- 什么虚函数?
- 如何使用多态?
- C++奇葩的设计(多态篇)
- 一些看起来挺实用,但是感觉不太会用的操作
- 多态的底层是如何实现的?
- 虚函数表
- 精准区分重载、重写,隐藏
- 面试或许会考察的题
- 唠唠家常
什么是多态?
下定义之前先讲个例子:
加入你是一个铲屎官,且家里有三个祖宗 🐱 、🐶 、🐰,那么在要干饭的时候,你为他们准备的食物分别是 猫粮,狗粮,胡萝卜。那么这里
为不同的宠物准备不一样的食物就可以看做多态
下定义:
在对待
同一件事
(喂食)的时候会有不同的结果
(食物)
z
如何才可以看到多态这一属性呢?(概念)
多态是在继承后的基础上拓展出来的,那么需要看这一个特性必须要满足以下的条件
- 必须是父子类(等于白说)
- 必须是
父类的指针或者引用
- 必须是
虚函数
上面三个条件缺一不可,少一个你都看不到多态这美丽的属性
示例代码:
class Shoveler
public:
virtual void foot()//条件3
cout<<"杂食,蔬菜,肉"<<endl;
;
class Cat:public Shoveler
public:
virtual void foot()//条件3
cout<<"猫粮,鱼"<<endl;
;
class Dog:public Shoveler
public:
virtual void foot()//条件3
cout<<"狗粮,骨头"<<endl;
;
void WhatEat(Shoveler &sl)//条件2
sl.foot();
int main()
Shoveler d1;
Cat d2;
Dog d3;
//调用
WhatEat(d1);
WhatEat(d2);
WhatEat(d3);
结果:
什么虚函数?
在上一篇文章继承中,有一个概念叫菱形继承,可以用虚继承来解决。那你还记得那个关键字吗?virtual
大胆猜想
给继承时加virtual 就是虚继承,那么在函数前加virtual那不就是虚函数,我真聪明,emmm似乎确实如此
如何使用多态?
上述的代码大致你已经知道他的基本使用了,但是那个还不够,毕竟只是一个示范罢了 !!!!
条件2
问
虽然这里时条件,但是我还是好想知道为什么,指针和引用可以但是类型不可以呢
答
这里涉及到了底层,本文后面回、会讲解,就带着这个疑问继续阅读
问
那他是如何调用的虚函数达到多态的呢?
答
好家伙你这问到点上了,但是这还是涉及到底层。现在可以说这种达到多态的场景叫做
覆盖,重写
条件3
多态条件补充
如果要构成多态,那么他的虚函数一定要
函数名,返回值,参数 都相同
,当然返回值前加virtual
C++奇葩的设计(多态篇)
由上面刚刚补充的条件知道要构成多态虚函数需要满足函数名,返回值,参数 都相同
,但是这里emmm出岔子了
岔子1:
返回值不同也可以构成多态
,但是必须返回值是父子类
(虽然我也不知道为啥有这个操作),且有一个好听的专有名词协变
如图所示:
岔子2:
子类的虚函数可以不写
。这里的话其实还比较好理解,或许是C++协会的大大害怕你会写忘了(有点贴心),其实重要的是继承了父类的虚拟函数了,因为虚函数继承是接口继承就是直接覆盖你的函数声明
(但是建议还是写上为好,高质量c++编程🤪
)
岔子3:
析构函数的虚函数可以名称不同,其实在上篇文章继承中说过,构造函数会被转换成destruct,所以其实还是同名
问
为啥析构函数还要是虚函数呀,好奇???
答
看下面这个例子
这种情况如果析构不构成重写的话,那么就会如图所示
一些看起来挺实用,但是感觉不太会用的操作
final
- 这个关键修饰的类不可继承
- 关键字修饰的虚函数不可以多态(重写,覆盖)
用法
override
关键修饰的虚函数
如果没有实现多态就会报错
,类似assert
用法
抽象类
在
虚函数声明
后面➕ =0
就是抽象类,当是抽象类的时候是不可以实例化对象的
,继承后如果子类不重写虚函数,那么子类也是抽象类
用法
抽象类就是一个媒介,或者他就是一个工具人,且只有被继承才可以使用,这里提个醒,他不可以实例化对象,
但是可以创建指针引用,且类中也是可以有变量
,好家伙真的工具类
多态的底层是如何实现的?
先看一些这道面试或许会问到的题(输出的是啥??
)
运行结果
解释:
这里为啥是16呢?其实是虚函数在搞鬼,有了虚函数后这个类就会在开始多一个指针(虚表指针)博主编译器是在64平台下的,如图所示内存模型
问
虚表指针好熟悉,哦继承中不是也有一个虚基表指针,虚表指针是不是就是存虚函数的指针?
答
不得不说你还是很聪明,没错他确实就是存虚函数的指针(
_vfptr
),但是你要吧虚表和虚基表区分开来,这是菱形继承解决方案中的虚继承,虚基表存的父类元素的是偏移量
虚函数表
概念:
每个类都有
属于自己的一个虚表
(毕竟有各自的作用域),且这个这个类实例化的对象共用一张虚表
,(虚函表存的这个函数的地址,而这个函数是存在公共代码段中的,所以一个类实例化的对象公用一张虚表)
上述留下的坑
- 为啥只有指针和引用才支持多态
- 多态的实现
为啥只有指针和引用才支持多态?
因为如果用对象去接收的换,那么就只子类拷贝构造父类,但是指针和引用还是志向子类的内存空间的,如图所示的内存模型视图:
对象接收的情况
这里你或许会疑问为啥不把虚表拷贝构造呢?
每个类有属于自己唯一的虚表,且本类实例化对象共用一个虚表,如果拷贝(实例化对象就会有问题,它的虚表改变了,自己本类的虚函数访问不到)
父类指针或者引用的情况(其实也就是底层)
精准区分重载、重写,隐藏
重载
必须是
同一作用域下
,函数名相同
,但是参数列表不同
,这个就是重载,有人会称他为静态的多态(在编译时就确定好函数的位置了)
重写
大前提条件就是在
继承的前提下
,父子类都有同名,同返回值,同参,虚函数
,调用的时候一定是父类的指针或者引用
隐藏
也是在继承的前提下,只要
父子类中有同名的函数
,不管参数和返回值是否一样
,直接构成隐藏
面试或许会考察的题
请问下面程序的打印结果是啥?
答案
classA classB classC classD
解释
这里初始化看的
不是构造函数列表,而是继承的顺序
,编译器会优化A只初始化一次,至于为啥那就要看底层了
请问代码中的指针的偏移量(其实就是指针指向的位置)
A p1==p2==p3 B p1==p2!=p3 C p1==p3!=p2 D p1!=p2!=p3
答案
C
解释
如图所示:
请问下面打印的结果是啥?
答案
b->1
解释如图所示
如果这几道题对了恭喜,你这就是腾讯之前考过的笔试选择题
唠唠家常
这个就是面向对象的三大特性,其实理解起来也还好,也是全部语言通用的哦,那么就到这里吧!!!!
以上是关于[C++]面向对象语言三大特性--多态的主要内容,如果未能解决你的问题,请参考以下文章