下载第五章多态性和虚函数解读.docx
《下载第五章多态性和虚函数解读.docx》由会员分享,可在线阅读,更多相关《下载第五章多态性和虚函数解读.docx(17页珍藏版)》请在冰点文库上搜索。
下载第五章多态性和虚函数解读
实验四多态性与虚函数
一、实验目的和要求
1了解多态性在C++中的体现;
2掌握虚函数的应用;
3了解抽象类。
二、基本概念
多态性
多态是指同样的消息被不同类型的对象接收时导致不同的行为,所谓消息是指对类的成员函数调用,不同的行为是指不同的实现,也就是调用了不同的函数。
多态性可以分为四类:
重载多态、强制多态、包含多态和参数多态。
多态从实现的角度来讲可以划分为两类:
编译时的多态和运行时的多态。
编译时的多态性是在编译的过程中确定了同名操作的具体操作对象,也就是通过重载函数来实现的。
运行时的多态性是在程序运行过程中才动态地确定操作所针对的具体对象,就是用虚函数来实现的。
虚函数
虚函数是重载的另一种表现形式,它是一种动态的重载方式,它提供了一种更为灵活的多态性机制。
虚函数允许函数调用与函数体之间的联系在运行时才建立,也就是在运行时才决定如何动作,即所谓的“动态连接”。
一般虚函数成员的定义语法是:
virtual函数类型函数名(形参表)
虚函数的定义实际就是在原有的普通函数成员前面使用virtual关键字来限定,虚函数声明只能出现在类定义中的函数原型声明中,而不能在成员的函数体中。
运行过程中的多态需要满足三个条件,首先类之间应满足赋值兼容规则,其二是要声明虚函数,第三是要由成员函数来调用或者是通过指针、引用来访问虚函数。
抽象类
抽象类是一种特殊的类,它为一族类提供统一的操作界面。
一个抽象类自身无法实例化,也就是说我们无法定义一个抽象类的对象,只能通过继承机制,生成抽象类的非抽象派生类,然后再实例化。
抽象类是带有纯虚函数的类。
纯虚函数是一个基类中说明的虚函数,它在该基类中没有定义具体的操作内容,要求各派生类必须根据实际需要定义自己的版本,纯虚函数的声明格式为:
virtual函数类型函数名(参数表)=0;
声明为纯虚函数之后,基类中就不再给出函数的实现部分,它的实现是在它的派生类定义的。
对抽象类使用的几点规定如下:
1抽象类只能用作其他类的基类,不能建立抽象类对象;
2抽象类不能用作参数类型、函数返回类型或显式转换的类型;
3可以说明指向抽象类的指针和引用,此指针可以指向它的派生类,进而实现多态性
一般虚函数成员的定义语法是:
virtual函数类型函数名(形参表)
纯虚函数的声明格式为:
virtual函数类型函数名(参数表)=0;
三、程序例题
例题4.1有一个汽车类Vehicle,将它作为基类派生出小车类Car、卡车为类Truck和轮船类Boat,定义message()函数用来显示各类信息。
解:
程序设计如下:
(1)在基类汽车类Vehicle类中,定义一个虚函数message(),用来显示信息;
(2)在各个派生类中,重新定义message()函数,以显示各自的信息;
(3)在测试函数中,定义基类指针ptr,当它指向不同的对象时,则执行不同对象所对应类的成员函数message()。
程序实现:
#include
classVehicle//车辆类Vehicle定义
{public:
virtualvoidmessage(){cout<<"Vehiclemessage\n";}
private:
intwheels;
floatweigth;
};
//汽车类Car定义
classCar:
publicVehicle
{public:
voidmessage(){cout<<"Carmessage\n";}
private:
intpassenger_load;
};
//卡车类Truck定义
classTruck:
publicVehicle
{public:
voidmessage(){cout<<"Truckmessage\n";}
private:
intpassenger_load;
floatpayload;
};
//轮船类Boat定义
classBoat:
publicVehicle
{intpassenger_load;
public:
voidmessage(){cout<<"Boatmessage\n";}
};
voidmain()
{Vehiclevehicle,*ptr;//定义一个基类Vehicle对象vehicle和基类指针ptr
Carcar;//定义一个Car类对象car
Trucktruck;//定义一个Truck类对象truck
Boatboat;//定义一个Boat类对象boat
ptr=&vehicle;//将指针ptr指向基类对象
ptr->message();//调用基类成员函数
ptr=&car;//将指针ptr指向Car类对象
ptr->message();//调用Car类成员函数
ptr=&truck;//将指针ptr指向Truck类对象
ptr->message();//调用Truck类成员函数
ptr=&boat;//将指针ptr指向boat类对象
ptr->messag();//调用Boat类成员函数
}
程序运行结果:
例4.2从基类的图形类中派生出两个类。
一个派生类是矩形类,在该类中,基类的x,y做作矩形的长和宽;另一个派生类是圆类,在该类中,新添加了成员是半径radius,半径的值等于x,y的坐标到原点的距离。
程序实现:
#include
#include
classGraph//定义一个图形基类
{floatx,y;
public:
Graph(floatxx,floatyy)
{x=xx;
y=yy;
}
floatarea(){return0;}
};
classRect:
publicGraph//由图形类派生出立方体
{floatlength,width,high;
public:
Rect(floatx,floaty):
Graph(x,y)
{length=x;
width=y;
}
floatarea(){returnlength*width;}
};
classCircle:
publicGraph//由图形类派生出圆类
{doubleradius;
public:
Circle(floatx,floaty):
Graph(x,)
{radius=sqrt(x*x+y*y);}
doublearea(){return3.1416*radius*radius;}
};
voidmain()
{Graphs0(10,10);
Rects1(10,10);
Circles2(10,10);
cout<<"原面积="<cout<<"矩形面积="<cout<<"圆的面积="<cout<<"面积="<:
area()<}
程序运行结果为:
在上题中,虽然在这三个类中都有一个求面积的同名函数area(),函数所带的参数也相同,但它们是属于不同的类。
在编译时函数area()就确定了其对象,各个对象调用其相应的函数,因此所得的结果也不同。
这一点从上面的结果可以看出。
例题4.3修改上题的基类,将Graph类定义为抽象类,其他要求不变。
解:
程序具体设计如下:
(1)在抽象类Graph类中,定义两个纯虚函数Area()和Display()。
(2)在各派生类中,根据各自不同的要求,定义Area()和Display()函数的具体操作。
(3)在主函数中,定义基类的指针数组ptr[2],使它指向不同派生类的对象,以便在程序运行过程中,执行不同对象所对应的类的成员函数。
程序实现:
#include
#include
classGraph//定义一个图形基类
{doublex,y;
public:
Graph(doublexx,doubleyy)
{x=xx;
y=yy;
}
virtualdoubleArea()=0;//纯虚函数
virtualvoidDisplay()=0;//纯虚函数
};
classRect:
publicGraph//由图形类派生出立方体
{doublelength,width;
public:
Rect(doublex,doubley):
Graph(x,y)
{length=x;
width=y;
}
doubleArea(){returnlength*width;}
voidDisplay(){cout<<"矩形面积=";}
};
classCircle:
publicGraph//由图形类派生出圆类
{doubleradius;
public:
Circle(doublex,doubley):
Graph(x,y)
{radius=sqrt(x*x+y*y);}
doubleArea(){return3.1416*radius*radius;}
voidDisplay(){cout<<"圆形面积=";}
};
voidmain()
{Graph*ptr[2];;//定义抽象类指针
Rects1(10,10);//矩形类对象
Circles2(10,10);//圆形类对象
ptr[0]=&s1;//指针指向矩形类对象
ptr[1]=&s2;//指针指向圆形类对象
intflag=0,choice;
while(flag!
=3)
{cout<cout<<"1.矩形面积"<cout<<"2.圆形面积"<cout<<"3.退出"<cout<<"请选择(1-3):
";
cin>>choice;
if(choice==3)flag=3;
elseif(choice>=1&&choice<3)
{ptr[choice-1]->Display();
cout<Area()<}
elsecout<<"选择不在范围内,请重新选择。
"<}
}
程序运行结果:
例4.4计算教师工资。
教授类计算的是教授的工资,由该类派生出的讲师类计算的是讲师的工资。
解:
(1)程序设计了一个类Professor,其成员函数Preliminary()为虚函数,该函数用于计算教师的工资,Disp()用于显示教师的收入。
(2)设计一个Prelector类,它是从Professor类派生的,其中的Preliminary()函数用于计算讲师的收入,这时的Disp()用于显示讲师的收入。
定义一个Print()函数,通过传递参数的不同,可调用不同的显示函数。
就是说,在执行Print()时,根据p对象的不同,自动确定调用相应的成员函数。
程序实现:
#include
classProfessor
{protected:
intNo;
charname[10];
intf1,f2,f3,f;
public:
virtualvoidPreliminary()
{cout<<"编号:
";
cin>>No;
cout<<"姓名:
";
cin>>name;
f1=2000;
f2=500;
f3=100;
f=f1+f2-f3;
}
voidDisp()
{cout<<"基本工资:
"<cout<<"岗位津贴:
"<cout<<"实发工资:
"<}
};
classPrelecto:
publicProfessor
{public:
voidPreliminary()
{cout<<"编号:
";
cin>>No;
cout<<"姓名:
";
cin>>name;
f1=1500;
f2=300;
f3=80;
f=f1+f2-f3;
}
voidDisp()
{cout<<"基本工资:
"<cout<<"岗位津贴:
"<cout<<"实发工资:
"<}
};
voidPrint(Professor&p)
{p.Preliminary();
p.Disp();
}
voidmain()
{Professorp1;
Prelectorp2;
cout<<"教授的工资:
"<Print(p1);//计算教授的工资
cout<<"讲师的工资:
"<Print(p2);//计算讲师的工资
}
程序运行结果:
例题4.5程序设计了一个抽象类图形类,由这个抽象类派生出矩形、圆形和三角形三个类。
在抽象类中定义了公共数据x、y,这两个数据可作为矩形的长和宽;两数据平方和的平方根用作圆的半径,两数据还作三角形的底边和高。
解:
具体设计如下:
(1)在抽象类图形类中,定义两个纯虚函数,一个是Area()用于求各种图形面积;另一个是Disp()函数,用于显示结果。
它们的实现在派生类中完成。
(2)在各个派生类中,写出Area()和Disp()函数的具体算法。
(3)在主函数中定义一个基类指针数组,由它指向各派生类对象,通过这个指针可调用各类的Area()和Disp()函数版本,从而体现了多态性。
程序实现:
#include
#include
classShape//定义Shape抽象类
{protected:
//保护成员,其派生类可以访问
doublex,y;
public:
voidSetvalue(intm,intn)
{x=m;
y=n;
}
virtualfloatArea()=0;//定义纯虚函数
virtualvoidDisp()=0;//定义纯虚函数
};
classRectangle:
publicShape//定义矩形派生类
{doublesr;
public:
floatArea()//虚函数在矩形类中的定义版本
{sr=x*y;
return1;
}
voidDisp(){cout<<"矩形面积="<};
classCircle:
publicShape//定义圆的派生类
{doublesc;
public:
floatArea()//虚函数在圆类中的定义版本
{sc=3.14*sqrt(x*x+y*y);
return1;
}
voidDisp(){cout<<"圆的面积="<};
classTriangle:
publicShape//定义三角形派生类
{doublest;
public:
floatArea()//虚函数在三角形类中的定义版本
{st=x*y/2;
return1;
}
voidDisp(){cout<<"三角形:
"<};
voidmain()
{Rectangler;//定义矩形类对象
Circlec;//定义圆类对象
Trianglet;//定义三角形对象
intx,y;
intchoice;//用来接收所选择的选项号
Shape*p[3];//定义抽象类指针数组
p[0]=newRectangle;//将指针p[0]指向矩形类对象
p[1]=newCircle;//将指针p[1]指向圆类对象
p[2]=newTriangle;//将指针p[2]指向三角形对象
do{//循环选择选项
cout<<"1.求矩形面积\n";//显示选择菜单
cout<<"2.求圆的面积\n";
cout<<"3.求三角形面积\n";
cout<<"0.退出\n";
cout<<"请选择功能(0—3):
";
cin>>choice;//输入选项号
if(choice>=1&&choice<=3)
{cout<<"请输入xy的值:
";
cin>>x>>y;//输入x,y的值
p[choice-1]->Setvalue(x,y);//根据choice值决定调用哪
p[choice-1]->Area();//调用成员函数
p[choice-1]->Disp();
}
cout<<"\n";
}while(choice!
=0);
}
程序运行结果:
四实验题目
1定义一个图形类为抽象类,其具有求对象的面积和周长等基本功能(Area(),Perim()),在此基础上派生出圆形、三角形和矩形,分别求出它们的周长和面积。
2设计一个程序,该程序能找出优秀学生(成绩在90分以上)和优秀教师(发表论文3篇以上)。
当输入一系列学生或教师的记录后(姓名、成绩),将他们中优秀的人的姓名输出来。
3定义一个基类为哺乳动物类Mammal,其中有数据成员年龄、重量、品种,有成员函数move()、speak()等,以此表示动物的行为。
由这个基类派生出狗、猫、马、猪等哺乳动物,它们有各自的行为。
编程分别使各个动物表现出不同的行为。
要求如下:
(1)从基类分别派生出各种动物类,通过虚函数实现不同动物表现出的不同行为。
(2)今有狗CAIRN:
3岁,3kg;DORE:
4岁,2kg;猫CAT:
2岁,4kg;马:
HORSE,5岁,60kg;猪:
PIG,2岁,45kg。
(3)设置一个Mammal类数组,设计一个屏幕菜单,选择不同的动物或不同的品种,则显示出动物相对应的动作,直到选择结束。
(4)对应的动作中要先显示出动物的名称,然后显示年龄、重量、品种、叫声及其他特征。