从薪水说看虚函数与多态

      声明:文章依据及绝大部分内容来自候俊杰著的《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函数变为虚函数。

     

 

 

运行结果是符合期望值的:

 

 

      这正是虚函数的妙用。

      虚函数的意义:虚函数正是为了对原则一反其道而行之设计的。即到底调用哪一个函数取决于指针当前指向的类型而非指针的原始类型。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
某公司的员工有经理Manager、技术人员Technicist和营销人员SalsePerson,他们的薪金计算方法如下: 经理按月计酬,方法是:基本工资+奖金;技术人员按月计酬,方法是:基本工资;营销人员按月计酬,方法是:基本工资+销售利润*5%。 每类人员都有职工编号、姓名、性别、入职时间、职位、基本工资等数据,其中为入职时间定义Date类,并为该类重载运算符<<,实现入职时间的输入;各类人员使用统一接口getpay()计算各类人员的月薪。其次,设计一个统计并输出该公司每个人员某几个月薪金情况的报表类Report,该类提供add接口向Report类的容器中添加员工信息,并提供print接口用于输出每个员工的职工编号、姓名、性别、入职时间、职位和在设定的月份时间段中该员工的薪酬总额。为了方便实现查找功能,为Report类重载[]运算符的功能,下标值为职位,能根据职位信息查找出所有符合该职位的员工,并重载print接口,输出查找出的员工信息,信息包括职工编号、姓名、性别、入职时间、职位、基本工资。在主函数中对实现的类进行测试,首先,创建各类人员对象,通过Report类的add接口向报表中添加这些人员信息,然后通过Report类的print接口输出报表。其次测试报表的查找功能,输入要查找的员工职位信息,通过Report类的print接口输出查找到的员工基本信息报表。
某公司雇员(Employee)包括经理(Manager),技术人员(Technician)和销售员(Salesman)。开发部经理(DeveloperManger),既是经理也是技术人员。销售部经理(SalesManager),既是经理也是销售员。 以Employee类为基类派生出Manager,Technician和Salesman类;再进一步派生出Developermanager和Salesmanager类。 Employee类的属性包括姓名、职工号、工资级别,月薪(实发基本工资加业绩工资)。操作包括月薪计算函数(pay()),该函数要求输入请假天数,扣去应扣工资后,得出实发基本工资。 Technician类派生的属性有每小时附加酬金和当月工作时数,及研究完成进度系数。业绩工资为三者之积。也包括同名的pay()函数,工资总额为基本工资加业绩工资。 Salesman类派生的属性有当月销售额和酬金提取百分比,业绩工资为两者之积。也包括同名的pay()函数,工资总额为基本工资加业绩工资。 Manager类派生属性有固定奖金额和业绩系数,业绩工资为两者之积。工资总额也为基本工资加业绩工资。 而DeveloperManager类,pay()函数是将作为经理和作为技术人员业绩工资之和的一半作为业绩工资。 SalesManager类,pay()函数则是经理的固定奖金额的一半,加上部门总销售额与提成比例之积,这是业绩工资。 编程实现工资管理。特别注意pay()的定义和调用方法:先用同名覆盖,再用运行时多

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值