虚函数
前言
关键字
- virtual
正文
1 | class MoveObject{ |
在多态之前控制这种行为的时候,一般都是通过传入参数不同
1 | void move(NPCObject *obj){ |
学习了多态之后,因为有向下转型的特性:
1 | void move(MoveObject *obj){ |
我们就可以通过这种方式传递。
这里没有输出是因为派生类调用的是基类的move,基类的move里面我们没有输出项。
当我们设置基类的move函数为虚函数时,其实就是告诉编译器基类的这个函数是虚的,派生类可以自定义。
1 | class MoveObject{ |
看到基类的函数设置成虚函数之后,在派生类调用的时候就会自动匹配到派生类的函数。
本质上就是动态绑定,因为基类的函数并不是说从内存上被覆盖了,而是派生类有重写的情况下就用派生类的,没有的话还是会用基类的。
虚函数使用条件
virtual
只能写在类的内部生命或者定义,不能写在外部,也就是外部的全局函数。
调用类的对象是无法使用虚函数的,必须使用基类指针来实现虚函数的调用
虚函数在派生类和基类中必须具有相同的及参数列表
虚函数在派生类和基类中返回值要求基本一致,但是当返回类型为该类型的指针和引用时除外
虚函数不能是函数模板
override 后缀可以强制要求检查函数是重载
final 后缀可以终止函数的重载
1 | class MoveObject{ |
学习了虚函数之后,我们知道继承的时候也会带上,那么NPC中的这个Move其实也就是一个虚函数,只不过我们再次重写了。
编译器也能看得到,当然这么写没太大关系,但是等NPC被别的类继承的时候,虽然函数还是叫move,但是转到上一层没看到virtual这个关键字还以为它不是虚函数。
所以这个也要看编程习惯,如果基类中他是虚函数,那么派生类尽量就都要加上,方便被其他类派生的时候可以直观的看出。
然后final这个关键字是用于结束重载的,所以像是虚函数后面定义了这个,那么派生类就不用加virtual了,都已经取消重载了自然没有必要性。
override呢就比较有意思
1 | class MoveObject{ |
在正常情况下,NPC中的move待了参数,编译器可能会认为他是单独的存在。
可以看到编译器认为它是属于NPC类的成员。
但事实上我们是让他继承过来重写的,而不是造成无论是意义上还是代码上的问题
解决方法override就派上用场了,他会强制检查这个成员是不是继承过来的。
虚函数并不一定全部写在基类中,因为类可能不止派生一次,当派生了很多次的时候,难免会有一些有新的功能。
结语
虚函数在基类的private下,派生类就没法访问了,但虚函数在基类的public下,派生类改成private还是能用的,用的前期是通过指针,而不是直接.访问,毕竟派生类已经做成private。
但是在反复修改的时候其实间接的破坏了封装性,不利于维护。