C++特性之一:多态

面向对象特性之一:多态

面向对象有三大特性:封装、继承、多态,其中多态是非常重要的一部分,而且多态也是很多公司在招聘C++方向的程序员的时候,经常会问到的问题,记得京东面试的时候,面试官的第一个问题就是:什么是多态?当时七七八八也说了一些,但是还是感觉没有条理,如果能够条理清晰的给别人人阐述出来,那一定是个不小的加分项,所以今天总结一下,希望对各位有所帮助。
既然要描述多态,那么就要了解多态的概念, 多态:就是同一种事物在不同状态下的表现状态。
最简单的一个例子:“ * ”,指针变量的前面,就是解引用的意思:int *p = a;
    在两个变量中间,就是乘号的意思:a * b;
这也是最简单的一种多态。

多态的分类:

C++特性之一:多态_第1张图片
静态多态就是指在程序编译期间实现的多态的类型
动态多态就是指在程序运行期间才能实现的多态的类型

静态多态:

函数重载:C++特性之一:多态_第2张图片当两个函数构成重载的时候,接收到的参数的值不同,就会调用不同的函数

泛型编程:

反省编程最主要的内容就是模版,包括函数模版,类模版,模版乘产生之后,在编译的时候就会根据相应的模版参数生成对应的实例,这一块可以参照我的另外一遍博客:模版——函数模版、类模版,博客连接:点击打开链接

动态多态:

动态多态就是在程序的运行期间才能确定的多态的类型。而动态多态的实现就是:虚函数

虚函数:

使用virtual关键字修饰类的成员函数时,指明该函数为虚函数,派生类需要重新实现,编译器将实现动态绑定

首先我们来看下面一段代码:
#include 
using namespace std;

class Base
{
public:
	virtual void fun()
	{
		cout<<"Base::fun()"<Derivefun();//运行之后,会在这里报错,说:Derivefun不是Base的成员
}
此时,大家就会有点疑问,赋值兼容规则里面也有说过基类的指针是可以指向派生类的对象的。
所以“Base* b = &d”这句话是没有问题的,而且编译器的报错也不再这里,编译器报的错是Derivefun不是Base的
成员,但是我们指针b里面存放的就是派生类的对象d,原因就在于:此时在编译期间,编译器眼中,指针d的类型是
基类的类型,所以就会在基类里面寻找函数Derivefun函数,但是基类中并没有Derivefun函数,函数此时在派生类中,
所以就会报错,
然后我们再看下面一段代码:
class Base
{
public:
	virtual void fun()
	{
		cout<<"Base::fun()"<
这段代码结构和上面的一样,只是里面的函数有差异,派生类和基类都都有的函数有:fun,fun1,fun2,fun3;
派生类中有独有的Derivefun,基类中有独有的Basefun函数,
对于相同的函数:
//fun:都是虚函数
//fun1:都不是虚函数
//fun2:基类不是虚函数,派生类是虚函数
//fun3:基类是虚函数,派生类不是虚函数
此时,用基类的指针指向派生类的对象,分别调用这些函数,结果如下:
C++特性之一:多态_第3张图片
可以看出,当基类和派生类都不是虚函数的时候,调用基类的函数,
当基类和派生类都是虚函数的时候,调用派生类的函数
当其中有一个是虚函数,另外一个不是虚函数的时候,就会调用不是虚函数。

那么他动态绑定条件是什么呢?
1、通过基类类型的引用或者指针调用虚函数,
当基类指针指向派生类的对象时,静态类型就是基类的类型,动态类型就是派生类类型的指针
2、必须是虚函数派生类一定要重写基类的虚函数
Tip:重写不是重载,其区别如下
C++特性之一:多态_第4张图片

纯虚函数:

在虚函数的后面加上“ = 0 ”,此时函数就变成纯虚函数,纯虚函数没有函数体,拥有纯虚函数的类,成为抽象类,不能实例化出对象,只有
派生类继承并重新实现之后,派生类才可以实例化出对象。

总结:
1、派生类重写基类的虚函数实现多态,要求函数名、参数列表、返回值完全相同(协变除外)。
2、基类中定义了虚函数,在派生类中该函数始终保持虚函数的特性。
3、只有类的非静态成员函数才能定义为虚函数,静态成员函数不能定义为虚函数。
4、如果在类外定义虚函数,只能在声明函数时加virtual关键字,定义时不用加。
5、构造函数不能定义为虚函数
6、不要在构造函数和析构函数中调用虚函数,在构造函数和析构函数中,对象是不完整的,可能会
出现未定义的行为。
7、虚表是所有类对象实例共用的(?)
Tip:虚表的问题,在本人的另外一篇博客中详细讲解,这里铺开讲解篇幅太长。


限于编者水平,有很多不足之处,欢迎指正。
Tip:如需转载,请注明出处





你可能感兴趣的:(c++)