C++——多态

一、多态的概念

在C++中多态是一个非常重要的观念,简单来说就是多种形态。多态分为两种,一种是编译时多态(静态多态),另外一种则是运行时多态( 动态多态)。编译时多态就是之前说过的函数重载和函数模版,给他们传不同类型是参数,他们就能够调用不同的函数,通过参数的不同达到多种形态,也是因为函数参数的匹配过程是在编译时完成的,所以在编译期间调用的函数就被确定了,我们把这种多态叫做编译时多态。

运行时的多态则是,给一个函数传入不同的对象,这个函数会根据对象的不同 ,完成不同的动作,达到多种形态的目的。

二、多态的定义和实现

首先多态一定是要在继承的关系下进行的,也是是说这个函数的作用域是不同的,这里可以拿函数重载来作比较,函数重载是发生在同一作用域下的。也就是说要构成函数重载,这几个函数必须在同一作用域下,比如说是一个类的成员函数,那么构成重载的这几个函数一定属于同一个类。而想要实现多肽,这个函数必须有不同的作用域,也就是说这个函数必须存在在父类和子类中,他们的作用域是不同的。这是一个条件。

第二个条件就是,在调用这个函数的时候,必须要用父类对象的指针或者引用来调用函数,同时这个函数必须是一个虚函数,子类必须对这个虚函数进行重写。

三、虚函数

类成员函数前面加virtual修饰,那么这个函数就被称为虚函数。非成员函数前不能加virtual修饰。

虚函数的重写/覆盖是指,在派生类中有一个跟基类完全相同的虚函数(即派⽣类虚函数与基类虚函数的返回值类型、函数名字、参数列表完全相同),这个虚函数的函数体可以与父类的虚函数不同,此时称派生类重写了基类的虚函数。

要注意的是在重写基类虚函数时,派生类的虚函数不加virtual关键字,也可以构成重写(因为在继承时,基类的虚函数也被继承下来了,虽然派生类的虚函数没有用virtual修饰,但是它也有虚函数的属性),但是这是一种不规范的写法。

四、override 和 final关键字

C++对于重写的要求是很严格的,很多时候可能会因为一些小的疏忽导致无法构成重载,但是这种错误在编译期间是不会报出的,只会在程序运行后,没有得到预期的结果才能发现,因此在C++11中提供了override关键字,帮助用户检测这个函数是否重写,如果这个函数没有重写则会发生报错,这个关键字添加在参数列表的括号后边。如果我们不想让派生类重写这个虚函数,那么可以用final修饰。

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

六、纯虚函数和抽象类

在虚函数的后面加上=0,则这个函数为纯虚函数,纯虚函数不需要定义实现(实现了也没有什么意义因为要被派生类重写,但是在语法上可以实现,也就是说纯虚函数可以有函数体,但是它的函数体没有意义),只要声明即可。

包含有纯虚函数的类叫做抽象类,抽象类不能实例化出对象,如果派生类继承后不重写纯虚函数,那么派生类也是抽象类。纯虚函数在某种程度上强制了派生类重写虚函数,因为不重写实例化不出对象。

七、多态的原理

在有虚函数的对象中,对象除了存储有成员变量以外,还会会多存储一个指针,这个指针被称为虚表指针(虚函数表指针的本质是一个函数指针数组指针,它是一个指针,指向了一个虚函数表,而虚函数表是一个数组,这个数组内存储的数据类型是函数指针)。一个虚函数的类至少要有一个一个虚函数表指针,因为这个类的所有虚函数的地址都要存放到这个类对象的虚函数表里面。这个虚函数表是这个类的所有对象共享的,因此在对象中,存储的是一个指向虚函数表的指针。当子类继承了父类以后,子类会先继承父类的虚函数表,当子类重写了虚函数以后,会把重写以后的虚函数的地址覆盖到被重写的虚函数的位置,当子类有新的虚函数时,会把新的虚函数地址添加在虚函数表的后面。当一个类发生多继承时,这个类会有多个虚函数表,同时继承和重新覆盖的方式和单继承一样,但是如果自己有新的虚函数时,这个虚函数的地址被会添加到第一个虚函数表中。

多态实现的原来是,当一个基类的指针或者引用指向派生类时,会把派生类中的数据切割放入基类中,当通过这个指针或者引用调用虚函数时,首先编译器会判断是否满足多态的条件,当满足多态的条件时,会通过对象的虚表指针找到对应的虚函数地址,从而调用派生类对应的虚函数。

八、动态绑定和静态绑定

在编译时,对不满足多态条件(基类指针或者引用+调用虚函数)的函数调用,在编译时就完成绑定,也就是编译时就确定了调用的函数地址,这样叫做静态绑定。

满足多态的条件时,函数的调用是在运行时才完成绑定的,也就是运行时到指向的对象的虚函数表中找到要调用的函数地址,这样才叫做动态绑定

相关推荐
I_Am_Me_36 分钟前
【JavaEE初阶】线程安全问题
开发语言·python
运维&陈同学43 分钟前
【Elasticsearch05】企业级日志分析系统ELK之集群工作原理
运维·开发语言·后端·python·elasticsearch·自动化·jenkins·哈希算法
金士顿3 小时前
MFC 文档模板 每个文档模板需要实例化吧
c++·mfc
ZVAyIVqt0UFji4 小时前
go-zero负载均衡实现原理
运维·开发语言·后端·golang·负载均衡
loop lee4 小时前
Nginx - 负载均衡及其配置(Balance)
java·开发语言·github
SomeB1oody4 小时前
【Rust自学】4.1. 所有权:栈内存 vs. 堆内存
开发语言·后端·rust
toto4124 小时前
线程安全与线程不安全
java·开发语言·安全
水木流年追梦5 小时前
【python因果库实战10】为何需要因果分析
开发语言·python
人才程序员6 小时前
QML z轴(z-order)前后层级
c语言·前端·c++·qt·软件工程·用户界面·界面
w(゚Д゚)w吓洗宝宝了6 小时前
C vs C++: 一场编程语言的演变与对比
c语言·开发语言·c++