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
这个就是虚函数的作用