第五章 多态性与虚函数
5.1 多态性
C++中的多态性可以分为4类:参数多态、包含多态、重载多态和强制多态。前面两种统称为通用多态,而后面两种统称为专用多态。
多态性是指用一个名字定义不同的函数,这些函数执行不同但又类似的操作,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数。
5.2 向上类型转换
向上类型转换:把一个派生类的对象作为基类的对象来使用。
向上类型转换中有三点需要特别注意:
- 向上类型转换是安全的
- 向上类型转换可以自动完成
- 向上类型转换的过程中会丢失子类型信息
5.3 功能早绑定和晚绑定
多态从实现的角度可以划分为两类:编译时的多态和运行时的多态。
绑定:确定操作的具体对象的过程
按照绑定进行的阶段的不同,可以分为两种不同的绑定方法:功能早绑定和功能晚绑定
绑定工作在编译连接阶段完成的情况称为功能早绑定。(重载多态、强制多态和参数多态)
绑定工作在程序运行阶段完成的情况称为功能晚绑定。(包含多态)
一般而言,编译型语言 → 功能早绑定(函数调用速度很快,效率高,但缺乏灵活性)
解释性语言 → 功能晚绑定 (降低了程序的运行效率,但增强了程序的灵活性)
C++采用了功能早绑定和功能晚绑定相结合的编译方法。
在C++中,
编译时的多态性主要是通过函数重载和运算符重载实现的。
运行时的多态性主要是通过虛函数来实现的。
5.4 实现功能晚绑定—虚函数
在派生类中重新定义时,其函数原型,包括返回类型、函数名、参数个数、参数类型的顺序,都必须与基类中的原型完全相同。
作用:允许在派生类中重新定义与基类同名的函数,并且可以通过指向基类对象的指针或基类对象的引用来访问基类和派生类中的同名函数。
- 派生类应该从它的基类公用派生。(赋值兼容规则成立的前提条件是派生类从其基类公用派生。)
- 必须首先在基类中定义虛函数
- 派生类对基类声明的成员函数进行重新定义得到的成员函数也是虛函数。
- 只有通过指向基类对象的指针或基类对象的引用访问虚函数时才能获得运行时的多态性。
- 虚函数必须是其所在类的成员函数,而不能是友元函数,也不能是静态成员函数
- 内联函数不能是虚函数
- 构造函数不能是虚函数(虚函数针对对象,构造函数是在对象产生之前运行的)
- 析构函数通常说明为虚函数
虚析构函数
virtual ~类名() ;
virtual ~Vehicle (){}
如果将基类的析构函数声明为虚函数时,由该基类所派生的所有派生类的析构函数也都自动成为虚函数
虚函数与重载函数的比较:
5.5 纯虚函数和抽象类
抽象类是带有纯虚函数的类。
纯虚函数是一个在基类中说明的虚函数,它在该基类中没有定义,但要求在它的派生类中必须定义自己的版本,或重新说明为纯虚函数。
class 类名
{...
virtual 函数类型 函数名(参数表)**=0;**
...
};
纯虛函数的函数体由派生类给出。
如果一个类至少有一个纯虚函数,那么就称该类为抽象类。
-
抽象类只能作为其他类的基类来使用,不能建立抽象类对象。
-
不允许从具体类(不包含纯虛函数的普通类)派生出抽象类。
-
抽象类不能用作参数类型、函数返回类型或显式转换的类型。
-
可以声明抽象类类型的指针,此指针可以指向它的派生类对象,进而实现动态多态性。
-
如果派生类中没有重新定义纯虚函数,则派生类只是简单继承基类的纯虚函数,则这个派生类仍然是一个抽象类。
如果派生类中给出了基类所有纯虚函数的实现,则该派生类就不再是抽象类了,它是一个可以创建对象的具体类。
-
在抽象类中也可以定义普通成员函数或虚函数,虽然不能为抽象类声明对象,但仍然可以通过派生类对象来调用这些不是纯虚函数的函数。
函数体为空的虚函数和纯虚函数的区别:
纯虚函数一般没有函数体,而空的虚函数的函数体为空。前者所在的类是抽象类,不能直接进行实例化,而后者所在的类是可以实例化的。它们共同的特点是都可以派生出新的类,然后在新类中给出新的虚函数的实现,而且这种新的实现可以具有多态特征。