C++虚函数的三点总结

1 . 如果你以一个「基础类别之指针」指向「衍生类别之对象」,那么经由该指针
你只能够调用基础类别所定义的函数。

2 . 如果你以一个「衍生类别之指针」指向一个「基础类别之对象」,你必须先做
明显的转型动作(explicit  cas t)。这种作法很危险,不符合真实生活经验,在
程序设计上也会带给程序员困惑。

3 . 如果基础类别和衍生类别都定义了「相同名称之成员函数」,那么透过对象指
针调用成员函数时,到底调用到哪一个函数,必须视该指针的原始型别而定,
而不是视指针实际所指之对象的型别而定。这与第1 点其实意义相通。

 

#include <string.h>
 class  CEmployee  //职员
 {
  private:
 char m_name[30];
 public:
   CEmployee();
   CEmployee(const char* nm) { strcpy(m_name, nm); }
 };

class  CWage : public CEmployee      //  时薪职员是一种职员
{
private :
float m_wage;
float m_hours;

public :
CWage(const char* nm)  : CEmployee(nm)  { m_wage = 250.0; m_hours = 40.0; }
void setWage(float wg)  { m_wage = wg; }
void setHours(float hrs)  { m_hours = hrs; }
float computePay();
};

class  CSales : public CWage     //  销售员是一种时薪职员
{
private :
float m_comm;
float m_sale;

public :
CSales(const char* nm) : CWage(nm)  { m_comm = m_sale = 0.0; }
void setCommission(float comm)      { m_comm = comm; }
void setSales(float sale)           { m_sale = sale; }
float computePay();
};

class  CManager : public CEmployee   // 经理也是一种职员
{
private :
float m_salary;
public :
CManager(const char* nm) : CEmployee(nm)  { m_salary = 15000.0; }
void setSalary(float salary)              { m_salary = salary; }
float computePay();
};

void  main()
{
CManager  aManager(" 陈美静");
CSales   aSales(" 朱羽明");
CWage    aWager(" 曾铭源");
}

虚拟函数的故事要从薪水的计算说起。根据不同职员的计薪方式,我设计computePay   函数如下:

 

float CManager::computePay()
{
return m_salary; //  经理以「固定周薪」计薪。
}
float CWage::computePay()
{
return (m_wage * m_hours); //  时薪职员以「钟点费* 每周工时」计薪。
}
float CSales::computePay()
{
// 销售员以「钟点费* 每周工时」再加上「佣金* 销售额」计薪。
return (m_wage * m_hours + m_comm * m_sale); //  语法错误。
}

CSal es   对象不能够直接取用CWage   的m _wage   和m _hours ,因为它们是private成员变量。所以是不是应该改为这样:
float CSales::computePay()
{
return CWage::computePay() + m_comm * m_sale;
}

这就合乎逻辑了:销售员是一般职员的一种,他的薪水应该是以时薪职员的计薪方式作为底薪,再加上额外的销售佣金。我们看看实际情况,如果有一个销售员:
CSales aSales(" 朱羽明");

那么底薪应该是:
aSales.CWage::computePay(); //  这是销售员的底薪。注意语法。
而全薪应该是:
aSales.computePay(); //  这是销售员的全薪
结论是:要调用父类别的函数,你必须使用s cope  re s ol ut ion opera tor (::)明白指出。
接下来我要触及对象类型的转换,这关系到指针的运用,更直接关系到为什么需要虚拟函数。

假设我们有两个对象:
CWage aWager;
CSales aSales(" 朱羽明");
销售员是时薪职员之一,因此这样做是合理的:
aWager = aSales; //  合理,销售员必定是时薪职员。
这样就不合理:
aSales = aWager; //  错误,时薪职员未必是销售员。
如果你一定要转换,必须使用指针,并且明显地做型别转换(cas t)动作:
CWage* pWager;
CSales* pSales;
CSales aSales(" 朱羽明");
pWager = &aSales; //  把一个「基础类别指针」指向衍生类别之对象,合理且自然。
pSales = (CSales *)pWager; //  强迫转型。语法上可以,但不符合现实生活。

如果你以一个「基础类别之指针」指向一个「衍生类别之对象」,那么经由此指针,你就只能够调用基础类别,比较不要脸,专门用爸爸的。。。。。

CSales aSales(" 朱羽明");
CSales* pSales;
CWage* pWager;
pSales = &aSales;
pWager = &aSales; //  以「基础类别之指针」指向「衍生类别之对象」
pWager->setSales(800.0); //  错误(编译器会检测出来),
// 因为CWage  并没有定义setSales  函数。
pSales->setSales(800.0); //  正确,调用CSales::setSales  函数。

那肿么办捏?看下面这个例子:

 

#include <iostream.h>
class CShape
{
public:
virtual void display() { cout << "Shape \n"; }
};
class CEllipse : public CShape
{
public:
virtual void display() { cout << "Ellipse \n"; }
};
class CCircle : public CEllipse
{
public:
virtual void display() { cout << "Circle \n"; }
};

class CTriangle : public CShape
{
public:
virtual void display() { cout << "Triangle \n"; }
};

 

 

class CRect : public CShape
{
public:
virtual void display() { cout << "Rectangle \n"; }
};
 class CSquare : public CRect
{
public:
virtual void display() { cout << "Square \n"; }
};
 void main()
{
CShape aShape;
CEllipse    aEllipse;
CCircle     aCircle;
CTriangle   aTriangle;
CRect       aRect;
CSquare     aSquare;
CShape* pShape[6] = { &aShape,
&aEllipse,
&aCircle,
&aTriangle,
&aRect,
&aSquare };

for (int i=0; i< 6; i++)
pShape[i]->display();
}

得到的结果是:

Shape
Ellipse
Circle
Triangle
Rectangle
Square

指针可以调用子类定义的虚函数了,要是去掉关键字virtual得到的结果是六个sharpe

这个就是虚函数的作用

转载于:https://my.oschina.net/icelily/blog/77622

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值