54虚函数与多态.docx

上传人:b****5 文档编号:2837138 上传时间:2022-11-15 格式:DOCX 页数:18 大小:24KB
下载 相关 举报
54虚函数与多态.docx_第1页
第1页 / 共18页
54虚函数与多态.docx_第2页
第2页 / 共18页
54虚函数与多态.docx_第3页
第3页 / 共18页
54虚函数与多态.docx_第4页
第4页 / 共18页
54虚函数与多态.docx_第5页
第5页 / 共18页
点击查看更多>>
下载资源
资源描述

54虚函数与多态.docx

《54虚函数与多态.docx》由会员分享,可在线阅读,更多相关《54虚函数与多态.docx(18页珍藏版)》请在冰豆网上搜索。

54虚函数与多态.docx

54虚函数与多态

5.4虚函数与多态

多态是面向对象程序设计的三大重要特征之一。

作为C++中的术语,多态是指使用相同的函数名来访问函数不同实现的方法。

正因为如此,多态也可以被描述为“一种接口,多种方法”。

也就是说可以使用相同的形式来访问一组通用的运算,即使与每个运算对应的具体行为可能不同。

C++支持编译时多态和运行时多态。

运算符重载和函数重载就是编译时多态的一种表现。

虽然运算符重载和函数重载的功能很强大,但它们不能满足一个完整的面向对象语言的全部需求。

因此,在C++中除了实现编译时多态之外,还使用派生类和虚函数来实现运行时多态,这也是本章的主题。

本章首先将简短地讨论指向派生类型的指针,因为它们提供了对运行时多态的支持。

5.4.1指向派生类型的指针

基类型的指针可以指向其派生类的对象。

运行时多态的基础是基类型指针。

基类型指针和派生类型指针可以通过多种方法进行关联,而其他类型的指针则不能。

在前面讲到,某个类型的指针一般不能指向其他类型的对象,但基类型指针和派生类型指针除外。

在C++中,基类型指针可以指向任何派生类型的对象。

例如,假设有一个基类B_class和一个B_class的派生类D_class。

在C++中,所有被声明为指向B_class类型的指针同时也可以指向D_class类型。

因此,假设:

B_class*p;//B_class类型的指针

B_classB_ob;//B_class类型的对象

D_classD_ob;//D_class类型的对象

那么下面两条语句是完全合法的:

p=&B_ob;//p指向B_class类型的对象

p=&D_ob;/*p指向D_class类型的对象,这个类派生于B_class*/

在上面的代码中,p可以用来访问D_ob中所有从B_ob继承的成员,但不能访问D_ob中定义的成员。

下面我们再来看一个更具体的示例,下面的程序定义了一个基类B_class和一个派生类D_class,程序中使用了简单的类层次结构来保存作者姓名和书名。

//使用基类型指针访问派生类型对象

#include

#include//foroldercompilers,use

usingnamespacestd;

classB_class

{

charauthor[80];

public:

voidput_author(char*s){strcpy(author,s);}

voidshow_author(){cout<

};

classD_class:

publicB_class{

chartitle[80];

public:

voidput_title(char*num){

strcpy(title,num);

}

voidshow_title(){

cout<<"Title:

";

cout<

}

};

intmain()

{

B_class*p;

B_classB_ob;

D_class*dp;

D_classD_ob;

p=&B_ob;//基类型对象的地址

//通过指针访问B_class类型的对象

p->put_author("TomClancy");

//通过基类型指针访问D_class类型的对象

p=&D_ob;

p->put_author("WilliamShakespeare");

//输出保存在对象中的每个作者姓名

B_ob.show_author();

D_ob.show_author();

cout<<"\n";

/*由于函数put_title()和show_title()不属于基类,因此不能通过基类指针p来访问它们,我们必须直接或者像下面这样通过派生类型的指针访问这两个函数。

*/

dp=&D_ob;

dp->put_title("TheTempest");

p->show_author();//这里既可以使用指针p也可以使用指针dp

dp->show_title();

return0;

}

程序输出如下:

TomClancy

WilliamShakespeare

WilliamShakespeare

Title:

TheTempest

在上面的示例中,指针p被声明为指向B_class类型的指针。

同时,它可以指向一个派生类D_class类型的对象并且可以用来访问派生类中从基类继承的成员。

但要记住,基类型指针不能访问在派生类中定义的成员。

这就是为什么程序需要通过派生类型的指针dp来访问函数show_title()的原因。

如果想通过基类型指针来访问在派生类中定义的成员,必须先把它转换为派生类型的指针。

例如,下面这行代码就可以正确地调用D_ob的成员函数show_title()。

((D_class*)p)->show_title();

其中,在类型转换运算外部的圆括号是必需的,因为我们需要转换的是指针p的类型,而不是调用函数show_title()所返回的类型。

尽管这种指针类型的转换方式在技术上没有任何错误,但最好还是避免这样做,因为这容易混淆代码(事实上,大多数C++程序员都认为这是一种不好的形式)。

需要知道的另外一点是:

基类型指针可以指向任何派生类型的对象,反之则不然。

也就是说,我们不能使用派生类型的指针来访问基类型的对象。

指针的增量运算或减量运算依赖于指针被声明的类型。

因此,当基类型指针指向派生类型的对象时,指针的增量运算或减量运算将不会使指针指向下一个派生类型的对象,相反,它将指向(指针认为是这样)下一个基类型的对象。

因此,当基类型的指针指向派生类型对象时,指针的增量运算或减量运算是无效的。

基类型指针可以指向任何派生类型的对象,这一点很重要,它也是C++的基础。

我们马上将会学到,基类型指针的这种灵活性是C++得以实现运行时多态的关键。

派生类型的引用

与刚才描述的指针行为一样,基类型的引用也可以指向派生类型的对象。

最常见的用法是在函数参数的声明和定义中。

基类型的引用参数可以接收基类型的对象或其派生类型的对象。

5.4.2虚函数

运行时多态的实现需要将两个特征结合在一起,即继承和虚函数。

在前面章节中你已经学习了继承,下面我们就来学习虚函数。

在函数声明前加virtual可以声明虚函数。

虚函数是指在基类中使用virtual声明,并且在一个或多个派生类中被重新定义的函数。

这样,每个派生类可以拥有自己的虚函数定义。

我们最感兴趣的是:

在使用基类型指针(或引用)来调用虚函数时,虚函数所发生的行为。

在这种情况下,C++根据指针指向对象的类型来决定调用虚函数的哪个定义,并且这种决定是在运行时作出的。

这样,当指针指向不同的对象时,将会执行虚函数的不同定义。

换言之,是指针指向对象的类型(而不是指针的类型)来决定虚函数的调用。

因此,如果在一个基类中包含一个虚函数,并且这个基类有两个或多个派生类,那么当一个基类指针指向不同类型的对象时,虚函数的不同定义将被执行。

同理,基类型的引用也是如此。

在基类中声明虚函数需要在函数声明前加关键字virtual。

当虚函数在派生类中重新定义时,关键字virtual不需要重复(当然重复也不是个错误)。

定义了虚函数的类被称为多态类。

包含虚函数的类被称为多态类,这个术语同样适用于从多态类继承的类。

下面的程序给出了虚函数的用法:

//使用虚函数的示例。

#include

usingnamespacestd;

classbase{

public:

在类base中,函数who()被声明为虚函数,这说明该函数可以在派生类中重新定义。

因此,在类first_d和second_d中,who()分别被重新定义。

virtualvoidwho()//声明虚函数

{

cout<<"Base\n";

}

};

classfirst_d:

publicbase{

public:

voidwho()//重新定义first_d中的who()

{

cout<<"Firstderivation\n";

}

};

classsecond_d:

publicbase

{

public:

voidwho()//重新定义second_d中的who()

{

cout<<"Secondderivation\n";

}

在main()中,声明了4个变量:

base_obj,base类型的对象;

p,指向base类型对象的指针;first_obj和second_obj,分别是两个派生类型的对象。

};

intmain()

{

basebase_obj;

base*p;

p被赋予base_obj的地址,函数who()被调用。

由于who()被声明为虚函数,在运行时,C++根据p所指向的对象类型决定who()的哪个定义将被调用。

此时,p指向一个base类型的对象,所以base中定义的who()被执行。

first_dfirst_obj;

second_dsecond_obj;

p=&base_obj;

p->who();//访问base中的who

然后,p被赋予first_obj的地址。

前面讲过,基类型的指针可以指向任何派生类型的对象。

现在,当who()又被调用,C++再次检查p指向对象的类型,然后根据该类型决定调用相应的who()。

由于p指向一个first_d类型的对象,所以who()相应的定义被调用。

同理,当把second_obj的地址赋给p时,who()在second_d中的定义被执行。

p=&first_obj;

p->who();//访问first_d中的who

p=&second_obj;

p->who();//访问second_d中的who

return0;

}

程序输出如下:

Base

Firstderivation

Secondderivation

●记住:

程序是在运行时决定调用虚函数的哪个定义。

更进一步地说,这个决定只依赖于基类型指针所指向的对象的类型。

虚函数可由标准对象通过点运算符调用。

这就是说,在前面的示例中,可以通过下面的语句来访问函数who():

first_obj.who();

但是,这种调用虚函数的方法忽略了虚函数的多态性。

只有使用基类型指针访问虚函数的时侯,运行时多态才得以体现。

在派生类中重新定义虚函数时,被称为覆盖虚函数。

初看上去,虚函数在派生类中的重新定义好像是一种特殊的函数重载形式,但实际上并不是这样。

事实上,这两种方法有着本质的不同。

首先,重载函数必须在参数的类型或数量上不同,而重新定义的虚函数在参数的类型和数量上必须相同。

虚函数的原型与它重新定义的形式必须完全相同。

如果原型不同,那么函数只能被简单地认为是重载形式,从而失去了虚函数的特征。

另一个限制是,在定义虚函数的类中,虚函数必须声明为类的成员而不能是友员,但虚函数可以被声明为其他类的友员。

同样,析构函数可以是虚函数,但构造函数则不可以。

正因为在重载一般的函数和重新定义虚函数之间有着差异和限制,我们使用术语“覆盖(overriding)”来描述虚函数的重新定义。

5.4.2.1虚函数的继承

展开阅读全文
相关资源
猜你喜欢
相关搜索

当前位置:首页 > 工程科技 > 能源化工

copyright@ 2008-2022 冰豆网网站版权所有

经营许可证编号:鄂ICP备2022015515号-1