深入浅出之多态分析(笔记二)

 

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

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

 

ContractedBlock.gif ExpandedBlockStart.gif Code
#include <stdio.h>
#include 
<string.h>

class CEmployee
{
private:
    
char m_name[30];
public:
    CEmployee();
    CEmployee(
char* nm)
    {
         strcpy(m_name,nm);
         printf(m_name);
    }

};

class CWage:CEmployee
{
private:
    
int m_wage;
    
int m_hour;
public:
    CWage(
char* nm):CEmployee(nm)
    {
        m_wage
=10;
        m_hour
=100;
    }
    
void set(int wage,int hour)
    {
        m_wage
=wage;
        m_hour
=hour;
    }
};

class CSales:CWage
{
private:
    
int n_sale;
    
int n_hour;
public:
    CSales(
char* nm):CWage(nm)
    {
        n_sale
=20;
        n_hour
=100;
    }
    
void set(int sale,int hour)
    {
        n_sale
=sale;
        n_hour
=hour;
    }

};

class CManage:public CEmployee
{
private:
    
int m_salary;    
public:
    CManage(
char* nm):CEmployee(nm)
    {
        m_salary
=15000;
    }
    
void setsalary(int salary)
    {
        m_salary
=salary;
    }
};


void main()
{
    CManage 
*ma=new CManage("经理");
    CWage 
*wang=new CWage("时薪员");
    CSales 
*zhu=new CSales("销售员");
}

 

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

我们可以看到:

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

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

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

经理:固定周薪计算

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

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

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

 

ContractedBlock.gif ExpandedBlockStart.gif Code
int computerPay()
{
    
return m_wage*m_hour;
}

 

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

 

ContractedBlock.gif ExpandedBlockStart.gif Code
    int computerPay()
    {
        
return n_sale*n_hour+computerPay();
    }

 

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

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

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

CWage wage;

CSales sale;

我们能否这样做:

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

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

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

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

 

ContractedBlock.gif ExpandedBlockStart.gif Code
void main()
{
    CEmployee
* emp;
    CManage ma(
"经理");
    emp
=&ma;
    emp
->computerPay();

    CWage wang(
"时薪员");
    emp
=&wang;
    emp
->computerPay();

    CSales zhu(
"销售员");
    emp
=&zhu;
    emp
->computerPay();
}

 

但是,运行后,你会发现,生活并不是你想像中的那样,总是调用的是基类的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++源码,以方便大家调试:

 

ContractedBlock.gif ExpandedBlockStart.gif Code
#include <stdio.h>
#include 
<string.h>

class CEmployee
{
private:
    
char m_name[30];
public:
    CEmployee();
    CEmployee(
char* nm)
    {
         strcpy(m_name,nm);
         printf(m_name);
    }
    
virtual int computerPay()
    {
        printf(
"这是基类的计算方法");
        
return(2);
    }


};

class CWage:public CEmployee
{
private:
    
int m_wage;
    
int m_hour;
public:
    CWage(
char* nm):CEmployee(nm)
    {
        m_wage
=10;
        m_hour
=100;
    }
    
void set(int wage,int hour)
    {
        m_wage
=wage;
        m_hour
=hour;
    }
virtual    int computerPay()
    {
        
return m_wage*m_hour;
    }
};

class CSales:public CWage
{
private:
    
int n_sale;
    
int n_hour;
public:
    CSales(
char* nm):CWage(nm)
    {
        n_sale
=20;
        n_hour
=100;
    }
    
void set(int sale,int hour)
    {
        n_sale
=sale;
        n_hour
=hour;
    }
virtual    int computerPay()
    {
        
return n_sale*n_hour+CWage::computerPay();
    }

};

class CManage:public CEmployee
{
private:
    
int m_salary;    
public:
    CManage(
char* nm):CEmployee(nm)
    {
        m_salary
=15000;
    }
    
void setsalary(int salary)
    {
        m_salary
=salary;
    }

virtual    int computerPay()
    {
        printf(
"经理的计算方法");
        
return m_salary;
    }
};


void main()
{
    CEmployee
* emp;
    CManage ma(
"经理");
    emp
=&ma;
    emp
->computerPay();

    CWage wang(
"时薪员");
    emp
=&wang;
    emp
->computerPay();

    CSales zhu(
"销售员");
    emp
=&zhu;
    emp
->computerPay();
}

 

 

 

 

 

 

转载于:https://www.cnblogs.com/mfm11111/archive/2009/02/19/1394290.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值