C++——多态

一、多态的概念

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

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

二、多态的定义和实现

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

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

三、虚函数

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

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

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

四、override 和 final关键字

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

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

六、纯虚函数和抽象类

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

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

七、多态的原理

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

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

八、动态绑定和静态绑定

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

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

相关推荐
XiaoLeisj1 小时前
【JavaEE初阶 — 多线程】单例模式 & 指令重排序问题
java·开发语言·java-ee
励志成为嵌入式工程师2 小时前
c语言简单编程练习9
c语言·开发语言·算法·vim
捕鲸叉3 小时前
创建线程时传递参数给线程
开发语言·c++·算法
A charmer3 小时前
【C++】vector 类深度解析:探索动态数组的奥秘
开发语言·c++·算法
Peter_chq3 小时前
【操作系统】基于环形队列的生产消费模型
linux·c语言·开发语言·c++·后端
记录成长java4 小时前
ServletContext,Cookie,HttpSession的使用
java·开发语言·servlet
前端青山4 小时前
Node.js-增强 API 安全性和性能优化
开发语言·前端·javascript·性能优化·前端框架·node.js
青花瓷4 小时前
C++__XCode工程中Debug版本库向Release版本库的切换
c++·xcode
睡觉谁叫~~~4 小时前
一文解秘Rust如何与Java互操作
java·开发语言·后端·rust
音徽编程5 小时前
Rust异步运行时框架tokio保姆级教程
开发语言·网络·rust