声明:文章依据及绝大部分内容来自候俊杰著的《MFC深入浅出》第二章——C++的重要性质。这一章其中一部分从员工的薪水角度阐述C++的虚函数及多态存在的意义及作用,我认为实在太经典!在此稍作整理并加入自己的一点理解,备忘。
首先给出职员类:
一个前提:在真实世界中某些时候我们会以“一类职员”来总称销售员、时新职员、经理等等。为了某种便利,我们也会想以“一个通用的指针”表示所有可能的职员类型。比如:
CEmployee *pEmployee; //通用指针,“一类职员”
CSales sales("小白"); //特定职员——销售员
pEmployee = &sales; //合理,因为销售员就是一类职员
如何通过通用指针如何计算出小白的薪水呢?我们大多数时候都希望这样(为了通用性):pEmployee->computePay()。但实际上这是错误的!
晴天霹雳(原则一):如果你以一个“基类之指针”指向一个“派生类之对象”,那么经由此指针,你只能够调用基类(而不是派生类)所定义的函数。
依据这一原则,pEmployee虽然指向CSales类对象,但它本身属于CEmployee类,因为CEmployee类并没有定义 computePay函数,所以pEmployee不能调用computePay函数。
同样依据这一原则,即使基类已经定义了computePay函数,pEmployee->computePay()调用的是pEmployee::computePay(),这不符合我的期望——CSales:: computePay()才是小白的工资。
由此我们得到三个结论:
1、如果你以一个“基类之指针”指向“派生类之对象”,那么经由该指针你只能够调用基类所定义的函数。
2、如果你以一个“派生类之指针”指向一个“基类之对象”,你必须先做明显的转型操作(explict cast)。这种做法很危险,不符合真实生活经验,在程序设计上也会给程序员带来困惑。
3、如果基类和派生类都定义了“相同名称之成员函数”,那么通过对象指针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始类型而定,而不是视指针实际所指的对象的类型而定。
现在困扰我们的问题是:我知道CSales:: computePay()可以得到一个明确的工资,但如此通用性很差,缺乏一般化。我期望依旧能够以CEmployee指针代表每一种职员,而又能够在实际指向不同种类之职员时,调用到不同版本之computePay()。
这种一般化的需求正是多态性!
多态的实现正是靠虚函数来完成!(扯了半天,终于扯出来了- -|||)
先看程序,在CEmployee类中添加纯虚函数computePay(),使得各派生类中的computePay函数变为虚函数。
运行结果是符合期望值的:
这正是虚函数的妙用。
虚函数的意义:虚函数正是为了对原则一反其道而行之设计的。即到底调用哪一个函数取决于指针当前指向的类型而非指针的原始类型。