虚函数与多态性、纯虚函数

1.虚函数与多态性

因为鱼的呼吸是吐泡泡,和一般动物的呼吸不太一样,所以我们在fish类中重新定义breathe方法。我们希望如果对象是鱼,就调用fish类的breathe()方法,如果对象是动物,那么就调用animal类的breathe()方法。程序代码如例2-16所示(EX08.CPP)。

2-16

#include <iostream.h>

class animal

{

public:

     void eat()

     {

          cout<<"animal eat"<<endl;

     }

     void sleep()

     {

          cout<<"animal sleep"<<endl;

     }

     void breathe()

     {

          cout<<"animal breathe"<<endl;

     }

};

class fish:public animal

{

public:

     void breathe()

     {

          cout<<"fish bubble"<<endl;

     }

};

 

void fn(animal *pAn)

{

     pAn->breathe();

}

void main()

{

     animal *pAn;

     fish fh;

     pAn=&fh;

     fn(pAn);

}

我们在fish类中重新定义了breathe()方法,采用吐泡泡的方式进行呼吸。接着定义了一个全局函数fn(),指向animal类的指针作为fn()函数的参数。在main()函数中,定义了一个fish类的对象,将它的地址赋给了animal类的指针变量pAn,然后调用fn()函数。看到这里,我们可能会有些疑惑,照理说,C++是强类型的语言,对类型的检查应该是非常严格的,但是,我们将fish类的对象fh的地址直接赋给指向animal类的指针变量,C++编译器居然不报错。这是因为fish对象也是一个animal对象,将fish类型转换为animal类型不用强制类型转换,C++编译器会自动进行这种转换。反过来,则不能把animal对象看成是fish对象,如果一个animal对象确实是fish对象,那么在程序中需要进行强制类型转换,这样编译才不会报错。

读者可以猜想一下例2-16运行的结果,输出的结果应该是“animal breathe”,还是“fish bubble”呢?

运行这个程序,你将看到如图2.12所示的结果。

2.12  EX09程序的运行结果(一)

为什么输出的结果不是“fish bubble”呢?这是因为在我们将fish类的对象fh的地址赋给pAn时,C++编译器进行了类型转换,此时C++编译器认为变量pAn保存就是animal对象的地址。当在fn函数中执行pAn->breathe()时,调用的当然就是animal对象的breathe函数。

为了帮助读者更好地理解对象类型的转换,我们给出了fish对象内存模型,如图2.13所示。

当我们构造fish类的对象时,首先要调用animal类的构造函数去构造animal类的对象,然后才调用fish类的构造函数完成自身部分的构造,从而拼接出一个完整的fish对象。当我们将fish类的对象转换为animal类型时,该对象就被认为是原对象整个内存模型的上半部分,也就是图2.13中的“animal的对象所占内存”。当我们利用类型转换后的对象指针去调用它的方法时,自然也就是调用它所在的内存中的方法。因此,出现如图2.12所示的结果,也就顺理成章了。

现在我们在animal类的breathe()方法前面加上一个virtual关键字,结果如例2-17所示。

2-17

#include <iostream.h>

class animal

{

public:

     void eat()

     {

          cout<<"animal eat"<<endl;

     }

     void sleep()

     {

          cout<<"animal sleep"<<endl;

     }

     virtual void breathe()

     {

          cout<<"animal breathe"<<endl;

     }

};

class fish:public animal

{

public:

     void breathe()

     {

          cout<<"fish bubble"<<endl;

     }

};

 

void fn(animal *pAn)

{

     pAn->breathe();

}

void main()

{

     animal *pAn;

     fish fh;

     pAn=&fh;

     fn(pAn);

}

virtual关键字申明的函数叫做虚函数。运行例2-17这个程序,结果调用的是fish类的呼吸方法:

2.14  EX08程序的运行结果(二)

这就是C++中的多态性。当C++编译器在编译的时候,发现animal类的breathe()函数是虚函数,这个时候C++就会采用迟绑定(late binding)技术。也就是编译时并不确定具体调用的函数,而是在运行时,依据对象的类型(在程序中,我们传递的fish类对象的地址)来确认调用的是哪一个函数,这种能力就叫做C++的多态性。我们没有在breathe()函数前加virtual关键字时,C++编译器在编译时就确定了哪个函数被调用,这叫做早期绑定(early binding)。

C++的多态性是通过迟绑定技术来实现的,关于迟绑定技术,读者可以参看相关的书籍,在这里,我们就不深入讲解了。

C++的多态性用一句话概括就是:在基类的函数前加上virtual关键字,在派生类中重写该函数,运行时将会根据对象的实际类型来调用相应的函数。如果对象类型是派生类,就调用派生类的函数;如果对象类型是基类,就调用基类的函数。

2.纯虚函数

breathe()函数申明为纯虚函数,结果如例2-18所示。

2-18

class animal

{

public:

    void eat()

    {

        cout<<"animal eat"<<endl;

    }

    void sleep()

    {

        cout<<"animal sleep"<<endl;

    }

    virtual void breathe() = 0;

};

纯虚函数是指被标明为不具体实现的虚成员函数(注意:纯虚函数也可以有函数体,但这种提供函数体的用法很少见)。纯虚函数可以让类先具有一个操作名称,而没有操作内容,让派生类在继承时再去具体地给出定义。凡是含有纯虚函数的类叫做抽象类。这种类不能声明对象,只是作为基类为派生类服务。在派生类中必须完全实现基类的纯虚函数,否则,派生类也变成了抽象类,不能实例化对象。

纯虚函数多用在一些方法行为的设计上。在设计基类时,不太好确定或将来的行为多种多样,而此行为又是必需的,我们就可以在基类的设计中,以纯虚函数来声明此种行为,而不具体实现它。

注意:C++的多态性是由虚函数来实现的,而不是纯虚函数。在子类中如果有对基类虚函数的覆盖定义,无论该覆盖定义是否有virtual关键字,都是虚函数。

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值