实验4 友元函数及虚函数的应用.docx
- 文档编号:24843916
- 上传时间:2023-06-02
- 格式:DOCX
- 页数:18
- 大小:30.80KB
实验4 友元函数及虚函数的应用.docx
《实验4 友元函数及虚函数的应用.docx》由会员分享,可在线阅读,更多相关《实验4 友元函数及虚函数的应用.docx(18页珍藏版)》请在冰豆网上搜索。
实验4友元函数及虚函数的应用
实验4友元函数及虚函数的应用
4.1实验目的
1.掌握友元函数的定义方法
2.理解并掌握利用虚函数实现动态多态性和编写通用程序的方法
3.掌握静态数据成员的特性
4.2实验内容与步骤
1.上机实验题一
利用虚函数实现的多态性来求四种几何图形的面积之和。
这四种几何图形是:
三角形、矩形、正方形和圆。
几何图形的类型可以通过构造函数或通过成员函数来设置。
⑴分析
计算这四种几何图的面积公式分别是:
三角形的边长为W,高为H时,则三角形的面积为W*H/2;矩形的边长为W,宽为H时,则其面积为W*H;正方形的边长为S,则正方形的面积为S*S;圆的半径为R,其面积为3.1415926*R*R。
为设置几何图形的数据并求出几何图形的面积,需要定义一个包含两个虚函数的类:
classShape{
public:
virtualfloatArea(void)=0;//求面积
virtualvoidSetdata(float,float=0)=0;//设置图形数据
};
因面积的计算依赖于几何图形,故在类中只能定义一个纯虚函数Area。
同理,设置几何图形数据的函数Setdata也只能定义为虚函数。
把这个基类派生出其它几何图形类。
如派生出的三角形类为:
classTriangle:
publicShape{
floatW,H;//三角形边长为W,高为H
public:
Triangle(floatw=0,floath=0){W=w;H=h;}
floatArea(void){returnW*H/2;}
voidSetdata(floatw,floath=0){W=w;H=h;}
};
在派生类中定义了基类中两个虚函数的实现。
为了实现求面积和设置数据的多态性,必须定义一个类,该类中定义一个指向基类Shape的指针数组,其元素分别指向由基类Shape派生出的不同的几何图形类,并完成求出所有几何图形面积之和,以及设置参数的函数。
一个完整的参考程序如下:
#include
#include
classShape{
public:
virtualfloatArea(void)=0;//虚函数
virtualvoidSetdata(float,float=0)=0;//虚函数
};
classTriangle:
publicShape{
floatW,H;//三角形边长为W,高为H
public:
Triangle(floatw=0,floath=0)
{
W=w;H=h;
}
floatArea(void)//定义虚函数
{
returnW*H/2;
}
voidSetdata(floatw,floath=0)//定义虚函数
{
W=w;H=h;
}
};
classRectangle:
publicShape{
floatW,H;//矩形边长为W,高为H
public:
Rectangle(floatw=0,floath=0){W=w;H=h;}
floatArea(void)//定义虚函数
{
returnW*H;
}
voidSetdata(floatw,floath=0)//定义虚函数
{
W=w;H=h;
}
};
classSquare:
publicShape{
floatS;//正方形边长S
public:
Square(floata=0)
{
S=a;
}
floatArea(void)//定义虚函数
{
returnS*S/2;
}
voidSetdata(floatw,floath=0)//定义虚函数
{
S=w;
}
};
classCircle:
publicShape{
floatR;//圆的半径为R
public:
Circle(floatr=0)
{
R=r;
}
floatArea(void)//定义虚函数
{
return3.1415926*R*R;
}
voidSetdata(floatw,floath=0)//定义虚函数
{
R=w;
}
};
classCompute{
Shape**s;//指向基类的指针数组
public:
Compute()
{//给几何图形设置参数
s=newShape*[4];
s[0]=newTriangle(3,4);
s[1]=newRectangle(6,8);
s[2]=newSquare(6.5);
s[3]=newCircle(5.5);
}
floatSumArea(void);
~Compute();
voidSetdata(intn,floata,floatb=0)//A
{
s[n]->Setdata(a,b);
}//B
};
Compute:
:
~Compute()//释放动态分配的存储空间
{
for(inti=0;i<4;i++)deletes[i];
delete[]s;
}
floatCompute:
:
SumArea(void)
{
floatsum=0;
for(inti=0;i<4;i++)
sum+=s[i]->Area();//通过基类指针实现多态性
returnsum;
}
voidmain(void)
{
Computea;
cout<<"四种几何图形的面积="< a.Setdata(2,10);//设置正方形的边长 cout<<"四种几何图形的面积="< a.Setdata(0,10,12);//设置三角形的边长和高 cout<<"四种几何图形的面积="< a.Setdata(1,2,5);//设置正方形的长和宽 cout<<"四种几何图形的面积="< a.Setdata(3,15.5); cout<<"四种几何图形的面积="< } 程序中A行的Setdata函数属于函数重载,它不是虚函数。 该函数中的B行通过基类指针实现多态性。 ⑵上机要求 自己设计测试数据,测试程序的正确性。 ⑶写出实验报告。 2.上机实验题二 利用虚函数实现多态性,设计一个通用的双向链表操作程序。 链表上每一个结点数据包括: 姓名,地址和工资。 要求建立一条双向有序链表,结点数据按工资从小到大的顺序排序。 ⑴分析首先定义抽象类Object,并由其派生出包含题目要求的结点数据。 这两个派生类可定义为: classObject{//定义一个抽象类,用于派生描述结点信息的类 public: Object(){}//缺省构造函数 virtualintIsEqual(Object&)=0;//判两个结点是否相等 virtualvoidShow()=0;//输出一个结点上的数据 virtualintIsGreat(Object&)=0;//判两个结点的大小 virtual~Object(){}; }; classMenNode: publicObject{//由抽象类派生出描述结点数据的类 char*Name;//姓名 char*Addr;//地址 intSalary;//工资 public: MenNode(charn=0,chara=0,inys=0) {//完成数据初始化 if(n==0)Name=0; else{ Name=newchar[strlen(n)+1];strcpy(Name,n); } if(a==0)Addr=0; else{ Addr=newchar[strlen(a)+1];strcpy(Addr,a); } Salary=s; } voidSetData(char*,char*,int);//重新设置结点的数据 intIsEqual(Object&);//判二个结点是否相等 intIsGreat(Object&ob);//判ob结点是否大于当前结点 ~MenNode()//释放动态分配的存储空间 { if(Name)delete[]Name;if(Addr)delete[]Addr; } voidShow()//重新定义虚函数 {cout<<"姓名: "< " < "< } }; 产生一个新结点时,首先要在链表上找到插入位置(按工资大小的升序),然后将新结点插入。 List类的定义参见教材。 完成插入的成员函数为: voidList: : AddNode(Node*node) { if(Head==0){//A Head=Tail=node;//使链表首和链表尾指针都指向这结点 node->Next=node->Prev=0;//指该结点的前后向指针置为空 } else{//链表不为空,找到插入位置 Node*pn=Head; while(pn){//B Object&obj=*(node->Info); if(pn->Info->IsGreat(obj)<=0)break;//C elsepn=pn->Next; } if(pn==0){//D Tail->Next=node;//使原链表尾结点的后向指针指向这结点 node->Prev=Tail;//使该结点的前向指针指向原链表尾结点 Tail=node;//使Tail指向新的链表尾结点 node->Next=0; } else{//插在pn所指向结点之前 if(pn==Head){//E node->Next=Head; Head->Prev=node; node->Prev=0; Head=node; } else{//F pn->Prev->Next=node;//使pn指向结点的前一个结点指向node node->Next=pn; node->Prev=pn;//设置后向链 pn->Prev=node; } } } } A行中条件成立时,双向链表为空链,要插入结点为链表上的第上一个结点,初始化这个双向链表。 B行中的循环语句实现查找插入位置,当找到插入位置或整个链表上的结点都查完后,结束这个循环语句。 C行中的条件成立时,表示要把结点插在pn所指向的结点之前。 D行中的条件成立时,表示要把结点插入链尾。 若E行中的条件成立时,要把结点插在第一个结点之前;否则将结点插在pn所指向的结点之前。 一个完整的参考程序如下: #include #include classObject{//定义一个用于派生结点信息的抽象类 public: Object(){} virtualintIsEqual(Object&)=0;//判二个结点是否相等 virtualvoidShow()=0;//输出一个结点上的数据 virtualintIsGreat(Object&)=0;//判二个结点的大小 virtual~Object(){}; }; classNode{//结点类 private: Object*Info;//指向描述结点的数据域 Node*Prev,*Next;//用于构成链表的前后向指针 public: Node(){Info=0;Prev=0;Next=0;} Node(Node&node)//完成拷贝功能的构造函数 { Info=node.Info;Prev=node.Prev;Next=node.Next; } voidFillInfo(Object*obj){Info=obj;}//使Info指向数据域 friendclassList;//定义友元类 }; classList{//实现双向链表操作的类 Node*Head,*Tail;//链表首和链表尾指针 public: List(){Head=Tail=0;}//置为空链表 ~List(){DeleteList();}//释放链表占用的存储空间 voidAddNode(Node*);//在链表尾加一个结点 Node*DeleteNode(Node*);//删除链表中的一个指定的结点 Node*LookUp(Object&);//在链表中查找一个指定的结点 voidShowList();//输出整条链表上的数据 voidDeleteList();//删除整条链表 }; voidList: : AddNode(Node*node) { if(Head==0){//条件成立时,为空链表 Head=Tail=node;//使链表首和链表尾指针都指向这结点 node->Next=node->Prev=0;//指该结点的前后向指针置为空 } else{//链表不为空,找到插入位置 Node*pn=Head; while(pn){ Object&obj=*(node->Info); if(pn->Info->IsGreat(obj)<=0)break; elsepn=pn->Next; } if(pn==0){//插入链尾 Tail->Next=node;//使原链表尾结点的后向指针指向这结点 node->Prev=Tail;//使该结点的前向指针指向原链表尾结点 Tail=node;//使Tail指向新的链表尾结点 node->Next=0; } else{//插在pn所指向结点之前 if(pn==Head){//插在第一个结点之前 node->Next=Head;Head->Prev=node; node->Prev=0;Head=node; } else{//使pn指向结点的前一个结点指向node pn->Prev->Next=node; node->Next=pn; node->Prev=pn;//设置后向链 pn->Prev=node; } } } } Node*List: : DeleteNode(Node*node)//删除指定的结点 { if(node==Head)//二者相等,表示删除链表首结点 if(node==Tail)//二者相等,表示链表上只有一个结点 Head=Tail=0; else{//删除链表首结点 Head=node->Next; Head->Prev=0; } else{//删除的结点不是链表上的首结点 node->Prev->Next=node->Next;//从后向链指针上取下该结点 if(node! =Tail)node->Next->Prev=node->Prev; elseTail=node->Prev;//要删除的结点为链表尾结点 } node->Prev=node->Next=0;//将已删除结点的前后向指针置为空 return(node); } Node*List: : LookUp(Object&obj)//从链表上查找一个结点 { Node*pn=Head; while(pn){ if(pn->Info->IsEqual(obj))returnpn;//找到要找的结点 pn=pn->Next; } return0;//链表上没有要找的结点 } voidList: : ShowList()//输出链表上各结点的数据值 { Node*p=Head; while(p){ p->Info->Show();p=p->Next; } } voidList: : DeleteList()//删除整条链表 { Node*p,*q; p=Head; while(p){ deletep->Info;//释放描述结点数据的动态空间 q=p;p=p->Next; deleteq;//释放Node占用的动态空间 } } classMenNode: publicObject{//由抽象类派生出描述结点数据的类 char*Name;//姓名 char*Addr;//地址 intSalary;//工资 public: MenNode(char*n=0,char*a=0,ints=0) { if(n==0)Name=0; else{ Name=newchar[strlen(n)+1];strcpy(Name,n); } if(a==0)Addr=0; else{ Addr=newchar[strlen(a)+1];strcpy(Addr,a); } Salary=s; } voidSetData(char*,char*,int); intIsEqual(Object&); intIsGreat(Object&); ~MenNode() { if(Name)delete[]Name; if(Addr)delete[]Addr; } voidShow()//重新定义虚函数 {cout<<"姓名: "< "地址: "< "< } }; voidMenNode: : SetData(char*n,char*a,ints) { if(Name)delete[]Name; if(Addr)delete[]Addr; if(n==0)Name=0; else{ Name=newchar[strlen(n)+1];strcpy(Name,n); } if(a==0)Addr=0; else{ Addr=newchar[strlen(a)+1];strcpy(Addr,a); } Salary=s; } intMenNode: : IsEqual(Object&obj)//定义比较结点是否相等的虚函数 { MenNode&temp=(MenNode&)obj; return(Salary==temp.Salary);//相等返回1,否则返回0 } intMenNode: : IsGreat(Object&obj)//定义比较结点大小的虚函数 { MenNode&temp=(MenNode&)obj; return(temp.Salary-Salary); } voidmain(void) { MenNode*p; Node*pn,*pt,node; Listlist; for(inti=1;i<5;i++){//建立包含五个结点的双向链表 p=newMenNode;//动态建立一个IntOb类的对象 charname[20],addr[40]; ints; cout<<"输入姓名,地址和工资: "; cin.getline(name,20); cin.getline(addr,40); cin>>s;cin.get(); p->SetData(name,addr,s); pn=newNode;//建立一个新结点 pn->FillInfo(p);//填写结点的数据域 list.AddNode(pn);//将新结点加入链表尾 } list.ShowList();//输出链表上各结点的数据值 cout<<'\n'; MenNodeda; da.SetData("zhang","NanJin",2000);//置要查找的结点数据值 pn=list.LookUp(da);//从链表上查找指定的结点 if(pn)pt=list.DeleteNode(pn);//若找到,则从链表上删除该结点 list.ShowList();//输出已删除结点后的链
- 配套讲稿:
如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。
- 特殊限制:
部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。
- 关 键 词:
- 实验4 友元函数及虚函数的应用 实验 函数 应用