看上图,我们创建了四个类,有职员类,经理类,时薪员工类,销售员类。通过代码将一步步分析引入虚函数,纯虚函数,虚函数表,多态,抽象类。

为了解说的方便,我们先从c++程序代码分析,最后再来验证是否与.net的结果一致,欢ying您提出宝贵的意见。程序代码如下:

 

Code

 

运行后,我们跟踪了一下,其跟踪过程如图:

我们可以看到:

1.CSales对象zhu这个销售员,因为继承了CWage,而CWage又继承了CEmployee,所以CSales相当于拥有了基类的所有成员变量和成员函数。

2.当new一个派生类时,其构造函数的执行顺序都是从基类一层层往下执行,而析构函数则刚好相反。

如果你是公司的一个财务,那么如果现在要你计算经理,时薪员,销售员的工资,你会如何做呢?其中

经理:固定周薪计算

时薪员:钟点费×每周工时

销售员:钟点费×每周工时+佣金*销售额

如果是这样,设计一个成员函数computerpay(),这个成员函数可设计如下:

 

Code

 

这是表示计算时薪员工资,如果我想用此方法表示销售员工资呢,能否这样写:

 

Code

 

很显然,其中的computerPay具有不确定性,编译器没那么智能到能自动判断究竟是哪个类中的方法,所以要调用父类的函数,必须使用CWage::computerPay()

接下来,该虚函数登场了,我们先来分析一下为什么要使用虚函数?

假设我们有两个对象,一个是时薪员,一个是销售员,表示如下:

CWage wage;

CSales sale;

我们能否这样做:

wage=sale;合理,为什么?因为销售员是属于时薪员

sale=wage;不合理,因为时薪员不一定是属于销售员

我们把wage=sale类似这样的情况,称为基类的指针指向派生类的对象。

现实世界中,我们总用动物来形容猫,狗,狐,狼等,那么我们能不能创建一个通用的指针来表示把有的职员类型呢。所以我们可以用如下代码来表示:

 

Code

 

但是,运行后,你会发现,生活并不是你想像中的那样,总是调用的是基类的ComputerPay方法。由此我们总结以下几点:

1.如果你以基类的指针指向派生类的对象,那么调用的总是基类所定义的函数。

2.如果你以派生类的对象指向基类的指针,这种不切合实际,往往最危险且容易使人迷糊。

3.如果基类和派生类调用了相同名称的成员函数,那么调用时视指针的原始类弄而定,而不是由指针所指的对象的类型而定。

如果我希望emp指向经理时,调用的是经理的computerPay();

              emp指向时薪员时,调用的是时薪员的computerPay();

              emp指向销售员时,调用的是销售员的computerPay();

那么此时我们应如何办呢:很简单,在所有类的computerPay方法前端加virtual修辞符。

这种做法就是虚函数,虚函数就是对“基类指针指向派生类对象时,总是调用基类所定义的方法”北道而弛的一种做法。如果你预期派生类中有可能重新定义某一个成员函数时,就是虚函数粉末登场的时候了。

看到了吗?我们以相同的指令却调用了不同的函数,这就是多态。编译器无法在编译时判断emp->computerPay()调用的是哪种方法,而只能在执行期才能判断,这称之为动态绑定。其它的变量和非虚函数在编译时就确认了固定地址的调用了,这称为静态绑定。

那么何时使用多态呢?

举一个例子:有一个图形类,他有几个派生类,如圆形,三角形,矩形。图形类有一个函数area();用来求面积,但是由于不同图形求面积的方法不同,要由具体的派生类来决定,所以可以把它定义为一个虚函数,由派生类来重载这个函数,所以不同的派生类里面area()函数的函数体是不同的。

让我们回头看看上一讲我们讲的Csharp例子,在上一讲我们谈到了CSharp形状基类,我们说它是抽象的,所以它根本就不该创建对象,因为没有任何意义,但为了在各派生类中绘图,我们又不得不加上display这个虚函数。所以在这种情况下你可以定义它什么都不做。看以下情况定义合理吗?如:

class CSharp

{

     public:

        virtual void display();{......}

}

不合理,因为这个函数根本就不应不调用,因为CSharp是抽像的,但我们又必须留一块空间给它(因为派生类要绘图用),在这样的情况下,就出现了纯虚函数,即virtual void display()=0即可。

好了,该说总结的时候了

1.如果你期望在派生类中重新定义方法,那么你应当在基类中声明虚函数。

2.以单一指令可调用不同的函数,这就是多态。

3.虚函数是实行动态与动态绑定的关健。

4.即然抽像类中的虚函数不打算被调用,那么我们可以设定它为纯虚函数。

5.我们可以说拥有纯虚函数者为抽像类。

6.抽像类不能产生对象,但我们可以定义一个基类指针指向派生类对象,对方便对派生类进行操作。

下面,附出这一节完整的c++源码,以方便大家调试:

 

Code

 

 

 

 

 

 

posted on 2009-02-19 20:22  jasonM  阅读(215)  评论(0编辑  收藏  举报