一、虚函数和多态
(1)虚函数:
在类的定义中,前面有virtual关键字的成员函数就是虚函数
注意:virtual关键字只出现在类定义的函数声明中,写函数体时不用
(2)多态
Ⅰ.多态的表现形式:
1.指针
上面提到,派生类的对象可以赋给基类指针,那么当我们用这个指针调用一个基类和派生类中同名同参的虚函数时:
①若该指针指向的是一个基类的对象,那么被调用的会是基类的虚函数
②若该对象指向的是一个派生类的对象,那么被调用的会是派生类的虚函数
2.引用
上面提到,派生类的对象可以赋给基类引用,那么当我们用这个引用调用一个基类和派生类中同名同参的虚函数时:
①若该引用的对象是一个基类的对象,那么被调用的会是基类的虚函数
②若该引用的对象是一个派生类的对象,那么被调用的会是派生类的虚函数
Ⅱ.多态的作用
在面向对象的程序设计中使用多态,能够增强程序的可扩充性,即程序需要修改或增加功能的时候,需要改动和增加的代码较少。
实战案例:北京大学程序设计魔兽世界大作业
注意事项:
1.在非构造函数,非析构函数的成员函数中调用虚函数,是多态!(因为在编译时并不会考虑构造函数和析构函数之外的函数调用)
2.在构造函数和析构函数中调用虚函数,不是多态。编译时即可确定,调用的函数是自己的类或基类中定义的函数,不会等到运行时才决定调用自己的还是派生类的函数。
3.派生类中和基类中虚函数同名同参数表的函数,不加virtual也自动成为虚函数
总的来说:“多态”的关键在于通过基类指针或引用调用一个虚函数时,编译时不确定到底调用的是基类还是派生类的函数,运行时才确定究竟调用哪个类下的函数,这种机制被叫做“动态联编”。
二、多态的实现原理
我们先来观察两个唯一区别在是否包含了虚函数的类的大小
发现含有虚函数的Base2会比不含虚函数的Base1多出8个字节
每一个有虚函数的类(或有虚函数的类的派生类)都有一个虚函数表,该类的任何对象中都放着虚函数表的指针。虚函数表中列出了该类的虚函数地址,多出来字节就是用来存放虚函数表的地址的。具体构造如下:
多态的函数调用语句被编译成一系列根据基类指针所指向的(或基类引用所引用的)对象中存放的虚函数表的地址,在虚函数表中查找虚函数地址,并调用虚函数的指令。
三、虚析构函数
通过基类的指针删除派生类对象时,通常情况下只会调用基类的析构函数。
但是当我们删除一个派生类的对象时,应该先调用派生类的析构函数,然后调用基类的析构函数。
这个时候我们就要人为地把基类的析构函数声明为virtual(派生类的析构函数可以不声明virtual),然后我们通过基类的指针删除派生类对象时,就会先调用派生类的析构函数再调用基类的析构函数。
四、纯虚函数和抽象类
(1)纯虚函数:带有纯说明符=0的虚函数,目的是为纯虚函数提供定义(若纯虚函数是析构函数那么必须提供定义).
注意:此定义必须要类体内部提供(函数声明的语法不允许纯说明符=0与函数体一起出现)
2.抽象类:包含纯虚函数的类叫做抽象类
注意:
①抽象类只能作为基类来派生新类使用,不能创建抽象类的对象
②抽象类的指针和引用可以指向由抽象类派生出来的类的对象
③在抽象类的成员函数内可以调用纯虚函数,但是在构造函数或析构函数内部不能调用纯虚函数。
④如果一个类从抽象类派生而来,那么当且仅当它实现了基类中的所有纯虚函数,它才能成为非抽象类。