C++学习:virtual虚函数
前言
学习面向对象编程,关键就是要理解封装、继承、多态这三种思想,在C++中,基类与派生类之间的多态性可以由virtual
关键字来实现
多态(polymorphism)
什么是多态?通俗地讲,多态就是“龙生九子,各有不同”。在当前语境下可以理解为:面对不同的实例,所执行的方法是不同的。
一个类作为基类,能够“生”出许多派生类。当我们传入某一个派生类的实例时,程序能够正确地执行与该派生类对应的方法。这就是所谓“运行时多态”
虚函数
定义
父类的某些成员函数,能在子类中被覆盖重写(override),在父类中声明时用virtual
修饰,在子类中可重定义,并且可以在函数名后面标注override
来提示。
如下实例:
1 |
|
我们为Ball基类派生了篮球、足球两个子类。从Ball类继承而来的getBallName方法并不能满足子类的需求,于是在两个子类中分别各自实现一次该方法。这就是虚函数的一般用法。
如果我们都用Ball类指针指向三个不同类的实例(父类指针指向子类对象)并调用该方法,程序也能够正确识别对象类型并正确链接到对应的定义去。
1 |
|
1 | Ball |
注意
子类中重写的函数,本质上还是一个虚函数,能够被继续继承和重写。有的程序员选择在子类中也在函数前标注
virtual
以作提示。事实上在重写函数时
override
的标注是非必需的,但是处于可读性还是写上吧:)无论是
virtual
还是override
,都只能在函数声明处使用一次,如果具体实现在其他源文件中,则源文件出不需也不能标注virtual
—来自期中考的惨痛教训~
为什么要用虚函数?
一般来说,父类指针指向子类对象时,由于C++ 静态绑定(Static Binding) 的特性(在编译、链接期就确定一个函数名对应的代码块),具有类型Ball
的指针只能调用Ball
对象的成员,即使指针指向的这个Ball
对象“实际上”是一个子类对象,甚至子类中有对应的同名成员函数重载。
为了让程序学会自己找到,需要使用虚函数,让程序在运行时通过 “运行时类型信息(RTTI, Runtime Type Information)” 机制,检索匹配调用函数的对象是什么类型,对应执行的是哪一个重载。实现所谓“动态绑定(Dynamic Binding)”。
纯虚函数
很多时候父类的虚函数甚至不需要具体实现,只提供一个声明(名字),具体实现交由各个派生类负责。可以想象成中央权力下放给地方。这种虚函数就叫纯虚函数。通过纯虚函数我们能在c++里面实现抽象基类(Abstract Base Class,简称ABC),甚至是面向对象中的接口(Interface)
用法
“猫说猫话,狗说狗话”
1 | class Animal |
直接写成形如virtual type foo() = 0;
的形式。
这种情况下我们根本不会实例化Animal基类。Animal被称作一个抽象类,而getVoice()
就是一个接口,只有实现了这个方法的子类才能实例化对象。