Giskard

(六)多态性与虚函数

2019-01-27

类型兼容规则是指在需要基类对象的任何地方,都可以使用公有派生类的对象来替代。

通过公有继承,派生类得到了基类中除构造函数、析构函数之外的所有成员。这样,公有派生类实际就具备了基类的所有功能,凡是基类能解决的问题,公有派生类都可以解决。

派生类的对象可以赋值给基类的对象。即用派生类对象中从基类继承来的成员,逐个赋值给基类对象的成员

同一基类的不同派生类对象之间不能赋值。

类型兼容

BaseClass baseObject;
DerivedClass derivedObject;
baseObject=derivedObject;

derivedObject.derivedFun();      //对
derivedObject.baseFun();          //对

baseObject.baseFun();              //对
baseObject.derivedFun();         //错

如果基类指针要访问派生类的成员函数,怎么办?

C++提供了多态机制来解决这个问题。

类型兼容规则是C++多态的重要基础。

多态是指具有相似功能的不同函数使用同一个名称来实现,从而可以使用相同的调用方式来调用这些具有不同功能的同名函数的特性。

C++支持的多态可以分为四种类型:

重载多态:函数重载和运算符重载

强制多态:强制类型转换

包含多态:虚函数

参数多态:函数模板和类模板

从实现的角度来划分

编译时多态:在编译阶段由编译系统根据操作数据确定调用哪个同名的函数。

运行时多态:在运行阶段根据产生的信息确定需要调用哪个同名的函数。

C++采用联编技术来支持多态。

联编是指把一个标识符名和一个存储地址联系在一起的过程。在多态的实现过程中,确定调用哪个同名函数的过程就是联编,又称绑定。

静态联编:在编译阶段完成的联编

动态联编:动态联编是指在程序执行之前,根据函数名和参数无法确定应该调用哪一个函数,必须在程序的执行过程中,根据具体的执行情况来动态地确定。

虚函数就是在基类中被关键字virtual说明、并在一个或多个派生类中被重新定义的成员函数。

若要访问派生类中相同名字的函数,必须将基类中的同名函数定义为虚函数,这样,将不同的派生类对象的地址赋给基类的指针变量后,就可以动态地根据这种赋值语句调用不同类中的函数

在程序运行时,不同类的对象调用各自的虚函数,这就是运行时多态。

实现动态的多态性时,必须使用基类类型的指针变量或对象引用,并使其指向不同的派生类对象,并通过调用指针或引用所指向的虚函数才能实现动态的多态性。

动态联编

当虚函数声明与实现分开时,virtual关键字只用在虚函数的声明中,不能用在虚函数的实现中。

只有通过对象指针或对象引用来调用虚函数,才能实现动态联编。如果采用对象来调用虚函数,则采用的是静态联编方式。

静态成员函数不能声明为虚函数。因为静态成员函数不属于某一个对象,没有多态性的特征

构造函数不能是虚函数。

内联成员函数不能声明为虚函数

析构函数可以是虚函数

虚析构

虚析构2

如果删除基类指针,就会调用该指针指向的派生类的析构函数,而派生类的析构函数又自动调用基类的析构

在派生类中重新定义虚函数时,必须保证函数值类型和参数与基类中的声明完全一致。

一个类中的虚函数说明只对派生类中重定义的函数有影响,对它的基类中的函数并没有影响。

一个基类或派生类的成员函数中可以直接调用该类等级中的虚函数,成员函数中调用虚函数采用动态联编

构造函数中调用虚函数采用静态联编。

抽象类

纯虚函数用virtual声明,没有任何实现、必须由派生类覆盖该函数提供实现

virtual void print()=0;

包含一个或多个纯虚函数的类称为抽象类。

如果派生类没有实现基类中的所有纯虚函数,派生类也是抽象类。

抽象类无法实例化,即不能创建抽象类的对象。

抽象类不能用作参数类型、函数值类型或显式转换的类型,但可以声明指向抽象类的指针或引用,通过指针或引用来指向并访问派生类对象,从而实现动态多态。

通过抽象类可以引用派生类的对象而不必知道确切的类型。

纯虚函数与函数体为空的虚函数的区别

前者没有函数体,而后者有函数体;

前者所在的类是抽象类,不能直接进行实例化,而后者所在的类是可以实例化的。

Tags: C/C++