-
实验6 多态性与虚函数
2018-06-23 19:14:46实验目的和要求 了解静态联编的动态联编的概念。掌握动态联编的条件。 实验内容 1.分析并调试下列程序。 //sy6_1.cpp #include<iostream> using namespace std; class Base { public: ...实验目的和要求
了解静态联编的动态联编的概念。掌握动态联编的条件。
实验内容
1.分析并调试下列程序。
//sy6_1.cpp #include<iostream> using namespace std; class Base { public: virtual void f(float x){cout<<"Base::f(float)"<<x<<endl;} void g(float x){cout<<"Base::g(float)"<<x<<endl;} void h(float x){cout<<"Base::h(float)"<<x<<endl;} }; class Derived:public Base { public: virtual void f(float x){cout<<"Derived::f(float}"<<x<<endl;} void g(int x){cout<<"Derived::g(int)"<<x<<endl;} void h(float x){cout<<"Derived::h(float)"<<x<<endl;} }; int main() { Derived d; Base *pb=&d; Derived *pd=&d; pb->f(3.14f); pd->f(3.14f); pb->g(3.14f); pb->h(3.14f); pd->h(3.14f); return 0; }
(1)找出以上程序中使用了重载和覆盖函数。
答:Base类中函数void g(); 和void h();与Derived类中的函数void g(); 和void h();函数名相同,参数类型不同,构成了函数重载。
(2)写出程序的输出结果,并解释输出结果。
输出结果解释:
pb和pd指向同一地址,它们运行结果应该是相同的,但实际运行出来的结果却不相同,原因是决定pb和pd调用函数运行结果的不是他们指向的地址,而是他们的指针类型。“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。在程序中pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::。
2. 分析并调试下列程序//sy6_3.cpp #include<iostream> using namespace std; class Base { public: void f(int x){cout<<"Base::f(int)"<<x<<endl;} void f(float x){cout<<"Base::f(float)"<<x<<endl;} virtual void g(void){cout<<"Base::g(void)"<<endl;} }; class Derived:public Base { public: virtual void g(void){cout<<"Derived::g(void}"<<endl;} }; int main() { Derived d; Base *pb=&d; pb->f(42); pb->f(3.14f); pb->g(); return 0; }
(1)找出以上程序中使用了重载和覆盖函数。
答:Base类中函数void f(); 在同一作用域中,函数名相同,参数类型不同,构成了函数重载。(2)写出程序的输出结果,并解释输出结果。
3. 分析并调试下列程序
写出程序的输出结果,并解释输出结果。//sy6_3.cpp #include<iostream> using namespace std; class Point { public: Point(double i,double j){x=i;y=j;} double Area(){return 0.0;} private: double x,y; }; class Rectangle:public Point { public: Rectangle(double i,double j,double k,double l):Point(i,j){w=k;h=l;} double Area(){return w*h;} private: double w,h; }; int main() { Point p(3.5,7); double A=p.Area(); cout<<"Area= "<<A<<endl; Rectangle r(1.2,3,5,7.8); A=r.Area(); cout<<"Area= "<<A<<endl; return 0; }
4. 分析并调试下列程序
//sy6_4.cpp #include<iostream> using namespace std; const double PI=3.1415; class Shap { public: virtual double Area()=0; }; class Triangle:public Shap { public: Triangle(double h,double w){H=h;W=w;} double Area(){return 0.5*H*W;} private: double H,W; }; class Rectangle:public Shap { public: Rectangle(double h,double w){H=h;W=w;} double Area(){return H*W;} private: double H,W; }; class Circle:public Shap { public: Circle(double r){R=r;} double Area(){return PI*R*R;} private: double R; }; class Square:public Shap { public: Square(double s){S=s;} double Area(){return S*S;} private: double S; }; double Total(Shap *s[],int n) { double sum=0; for(int i=0;i<n;i++) sum+=s[i]->Area(); return sum; } int main() { Shap *s[5]; s[0]=new Square(8.0); s[1]=new Rectangle(3.0,8.0); s[2]=new Square(12.0); s[3]=new Circle(8.0); s[4]=new Triangle(5.0,4.0); double sum=Total(s,5); cout<<"SUM = "<<sum<<endl; return 0; }
程序的输出结果如下:
(1)指出抽象类。
(2)指出纯虚函数,并说明它的作用。
(3)每个类的作用是什么?整个程序的作用是什么?5. 某学校对教师每个月工资的计算规定如下:固定工资+课时补贴;教授的固定工资为5000元,每个课时补贴50;副教授的固定工资为3000,每个课时补贴30元;讲师的固定工资为2000元,每个课时补贴20元。定义教师抽象类,派生不同职称的教师类,编写程序求若干个教师的月工资。(sy6_5.cpp)
//sy6_5.cpp #include <iostream> using namespace std; class teacher { public: virtual int salary()=0; virtual void print(int)=0; }; class professor:public teacher { public: professor() { cout<<"教授:"; cin>>name; cout<<"课时:"; cin>>lessons; }; int salary() { return (5000+lessons*50); }; void print(int money) { cout<<"职称:教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; private: char name[128]; int lessons; }; class associateprofessor:public teacher { private: char name[128]; int lessons; public: associateprofessor() { cout<<"副教授:"; cin>>name; cout<<"课时:"; cin>>lessons; }; int salary() { return (3000+lessons*30); }; void print(int money) { cout<<"职称:副教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; }; class Lecturer:public teacher { private: char name[128]; int lessons; public: Lecturer() { cout<<"讲师:"; cin>>name; cout<<"课时:"; cin>>lessons; }; int salary() { return (2000+lessons*20); }; void print(int money) { cout<<"职称:讲师 姓名:"<<name<<"薪水:"<<money<<endl<<endl; }; }; int main() { teacher *t = NULL; int money=0; t = new professor(); money = t->salary(); t->print(money); delete t; t = new associateprofessor(); money = t->salary(); t->print(money); delete t; t = new Lecturer(); money = t->salary(); t->print(money); delete t; t = NULL; return 0; }
输出结果:
分析与讨论
1.结合实验内容中第1题和第2题,说明重载与覆盖的区别。
重载与覆盖的区别:
1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。
3、覆盖要求参数列表相同;重载要求参数列表不同。
4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。2.总结静态联编和动态联编的区别和动态联编的条件。
答:静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。
动态联编的条件:
①必须把动态联编的行为定义为类的虚函数。
②类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。
③必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。 -
实验6,多态性与虚函数
2018-06-12 11:13:48一、实验目的和要求 了解静态联编和动态联编的概念。掌握动态联编的条件。二、实验内容1、分析并调试下列程序。 //sy6_1.cpp #include<iostream> using namespace std; class Base { public: ...一、实验目的和要求
了解静态联编和动态联编的概念。掌握动态联编的条件。
二、实验内容
1、分析并调试下列程序。
//sy6_1.cpp #include<iostream> using namespace std; class Base { public: virtual void f(float x){cout<<"Base::f(float)"<<x<<endl;} void g(float x){cout<<"Base::g(float)"<<x<<endl;} void h(float x){cout<<"Base::h(float)"<<x<<endl;} }; class Derived:public Base { public: virtual void f(float x){cout<<"Derived::f(float)"<<x<<endl;} void g(float x){cout<<"Derived::g(float)"<<x<<endl;} void h(float x){cout<<"Derived::h(float)"<<x<<endl;} }; int main() { Derived d; Base * pb=&d; Derived * pd=&d; pb->f(3.14f); pb->f(3.14f); pb->g(3.14f); pb->h(3.14f); pb->h(3.14f); return 0; }
(1)找出以上程序中使用了重载和覆盖的函数。
答:Base类中函数void g(); 和void h();与Derived类中的函数void g(); 和void h();函数名相同,参数类型不同,构成了函数重载。
(2)写出程序的输出结果,并解释输出结果。
2、分析并调试下列程序。
//sy6_2.cpp #include<iostream> using namespace std; class Base { public: void f(int x){cout<<"Base::f(int)"<<x<<endl;} void f(float x){cout<<"Base::f(float)"<<x<<endl;} virtual void g(void){cout<<"Base::g(void)"<<endl;} }; class Derived:public Base { public: virtual void g(void){cout<<"Derived::g(void)"<<x<<endl;} }; int main() { Derived d; Base * pb=&d; Derived * pd=&d; pb->f(42); pb->f(3.14f); pb->g(); return 0; }
(1)找出以上程序中使用了重载和覆盖的函数。
答:Base类中函数void f(); 在同一作用域中,函数名相同,参数类型不同,构成了函数重载。
(2)写出程序的输出结果,并解释输出结果。
3、分析并调试下列程序
//sy6_3.cpp #include<iostream> using namespace std; class Point { public: Point(double i,double j){x=i;y=j;} double Area(){return 0.0;} private: double x,y; }; class Rectangle:public Point { public: Rectangle(double i,double j,double k,double l):Point(i,j){w=k;h=l;} double Area(){return w*h;} private: double w,h; }; int main() { Point p(3.5,7); double A=p.Area(); cout<<"Area="<<A<<endl; Rectangle r(1.2,3,5,7,8); A=r.Area(); cout<<"Area="<<A<<endl; return 0; }
写出程序的输出结果,并解释输出结果。
4、分析并调试下列程序。
//sy6_4.cpp #include<iostream> using namespace std; const double PI=3.1415; class Shap { public: virtual double Area()=0; }; class Triangle:public Shap { public: Triangle(double h,double w){H=h;W=w;} double Area(){return 0.5*H*W;} private: double H,W; }; class Rectangle:public Shap { public: Rectangle(double h,double w){H=h;W=w;} double Area(){return 0.5*H*W;} private: double H,W; }; class Circle:public Shap { public: Circle(double r){R=r;} double Area(){}return PI*R*R;} private: double R; }; class Square:public Shap { public: Square(double s){S=s;} double Area(){}return S*S;} private: double S; }; double Total(Shap*s[],int n) { double sum=0; for(int i=0;i<n;i++) sum+=s[i]->Area(); return sum; } int main() { Shap *s[5]; s[0]=new Square(8.0); s[1]=new Square(3.0,8.0); s[2]=new Square(12.0); s[3]=new Square(8.0); s[4]=new Square(5.0,4.0); double sum=Total(s,5); cout<<"SUM="<<sum<<endl; return 0; }
运行结果:
(1)指出抽象类。
(2)指出纯虚函数,并说明它的作用。(3)每个类的作用是什么?整个程序的作用是什么?
5. 某学校对教师每个月工资的计算规定如下:固定工资+课时补贴;教授的固定工资为5000元,每个课时补贴50;副教授的固定工资为3000,每个课时补贴30元;讲师的固定工资为2000元,每个课时补贴20元。定义教师抽象类,派生不同职称的教师类,编写程序求若干个教师的月工资。(sy6_5.cpp)
//sy6_5.cpp #include <iostream> using namespace std; class Teacher { public: virtual int Salary()=0; virtual void Print(int)=0; }; class Professor:public Teacher { private: char name[128]; int lessons; public: Professor() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (5000+lessons*50); }; void Print(int money) { cout<<"职称:教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; }; class AssociateProfessor:public Teacher { private: char name[128]; int lessons; public: AssociateProfessor() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (3000+lessons*30); }; void Print(int money) { cout<<"职称:副教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; }; class Lecturer:public Teacher { private: char name[128]; int lessons; public: Lecturer() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (2000+lessons*20); }; void Print(int money) { cout<<"职称:讲师 姓名:"<<name<<"薪水:"<<money<<endl<<endl; }; }; int main() { Teacher *t = NULL; int money=0; t = new Professor(); money = t->Salary(); t->Print(money); delete t; t = new AssociateProfessor(); money = t->Salary(); t->Print(money); delete t; t = new Lecturer(); money = t->Salary(); t->Print(money); delete t; t = NULL; return 0; }
运行结果:
6. 把实验5中的第4题的Shape类定义为抽象类,提供共同操作界面的纯虚函数。TwoDimShape类和ThreeDimShape类仍然抽象类,第3层具体类才能提供全部函数的实现。在测试函数中,使用基类指针实现不同派生类对象的操作。
三、分析与讨论
1、结合实验内容中第1题和第2题,说明重载与覆盖的区别。
答:重载与覆盖的区别:1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。3、覆盖要求参数列表相同;重载要求参数列表不同。4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
2、总结静态联编和动态联编的区别和动态联编的条件。
答:静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。动态联编的条件:必须把动态联编的行为定义为类的虚函数;类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来;必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。
四、实验小结
通过本次实验我们了解了静态联编和动态联编的概念,学习了动态联编的条件。
-
C++实验6_2多态性与虚函数(2)
2018-11-10 21:42:11学习使用虚函数实现动态多态性; 二、实验环境(软、硬件及条件) 一台安装有Visual C++ 6.0的计算机 三、实验步骤 二、实验任务 1.定义Point类,有坐标x,y两个成员变量;对Point...一、实验目的和要求
1.进一步掌握运算符重载的方法。
2.学习使用虚函数实现动态多态性;
二、实验环境(软、硬件及条件)
一台安装有Visual C++ 6.0的计算机
三、实验步骤
二、实验任务
1.定义Point类,有坐标x,y两个成员变量;对Point类重载+、-、++(左)、++(右)四种运算符,实现对坐标值的运行与改变。
2.修改程序Lab.cpp,给bicycle、motorcar、motorcycle这三个类均定义Run、Stop等成员函数,观察虚函数的作用。
三、实验步骤
1.编写程序定义Point类,在类中定义整型的私有成员变量x、y,定义成员函数 Point operator + (Point p2)重载“+”运算符,定义成员函数 Point operator - (Point p2)重载“-”运算符,定义成员函数Point operator++( )重载“++(左)”运算符,定义成员函数 Point operator++(int )重载“++(右)”运算符,实现对坐标值的改变。注意观察++(左)与++(右)运算的结果是否有区别,试着重载--(左)与--(右)。
2.修改程序lab.cpp,给bicycle、motorcar、motorcycle这三个类均定义Run、Stop等成员函数,而main函数不变,编译运行,观察结果并作分析。
已知Lab.cpp如下:
#include <iostream.h>
class vehicle
{
private:
int MaxSpeed;
int Weight;
public:
vehicle(){MaxSpeed=0; Weight=0;};
~vehicle(){};
void Run() {cout << "Now it is running!" << endl; }
void Stop() {cout << "Now it has stopped!" << endl; }
};
class bicycle : virtual public vehicle
{
private:
int Height;
public:
bicycle(){};
~bicycle(){};
};
class motorcar : virtual public vehicle
{
private:
int SeatNum;
public:
motorcar(){};
~motorcar(){};
};
class motorcycle : public bicycle , public motorcar
{
public:
motorcycle (){};
~motorcycle (){};
};
void main()
{
vehicle a;
bicycle b;
motorcar c;
motorcycle d;
a.Run();
a.Stop();
b.Run();
b.Stop();
c.Run();
c.Stop();
d.Run();
d.Stop();
}
3.修改上面的main函数,定义一个基类的指针,再用这个指针分别调用这几个类的对象的成员函数,编译运行,观察结果并分析原因;把基类的Run、Stop定义为虚函数,再编译运行,观察结果并分析原因;把其他类的Run、Stop也定义为虚函数,看看是否有变化
四、实验中遇到的问题及解决
无
五、实验结果及分析
1.
#include<iostream>
using namespace std;
class Point
{private:
int x,y;
public:
Point(){x=0,y=0;};
Point(int m,int n){x=m,y=n;cout<<"("<<x<<","<<y<<")"<<endl;};
Point operator + (Point p2);
Point operator - (Point p2);
Point & operator++();
Point & operator++(int );
void show(){cout<<"("<<x<<","<<y<<")"<<endl;}
};
Point Point::operator + (Point p2){Point p1(x+p2.x,y+p2.y);return p2;};
Point Point::operator - (Point p2){Point p3(x-p2.x,y-p2.y);return p2;};
Point & Point::operator++(){x++,y++;return *this;};
Point & Point::operator++(int ){++x,++y;return *this;};
void main()
{Point g(3,6),f(8,9),h1=g+f,h2=g-f,m,n;
g++;
g.show();
g=++g;
g.show();
}
2.
#include <iostream.h>
class vehicle
{
private:
int MaxSpeed;
int Weight;
public:
vehicle(){MaxSpeed=0; Weight=0;};
~vehicle(){};
void Run() {cout << "Vehicle:Now it is running!" << endl; }
void Stop() {cout << "Vehicle:Now it has stopped!" << endl; }
};
class bicycle : virtual public vehicle
{
private:
int Height;
public:
bicycle(){};
~bicycle(){};
void Run() {cout << "Bicycle:Now it is running!" << endl; }
void Stop() {cout << "Bicycle:Now it has stopped!" << endl; }
};
class motorcar : virtual public vehicle
{
private:
int SeatNum;
public:
motorcar(){};
~motorcar(){};
void Run() {cout << "Motorcar:Now it is running!" << endl; }
void Stop() {cout << "Motorcar:Now it has stopped!" << endl; }
};
class motorcycle : public bicycle , public motorcar
{
public:
motorcycle (){};
~motorcycle (){};
void Run() {cout << "Motorcycle:Now it is running!" << endl; }
void Stop() {cout << "Motorcycle:Now it has stopped!" << endl; }
};
void main()
{
vehicle a;
bicycle b;
motorcar c;
motorcycle d;
a.Run();
a.Stop();
b.Run();
b.Stop();
c.Run();
c.Stop();
d.Run();
d.Stop();
}
3.
定义基类vehicle指针,只能访问bicycle和motorcar的成员函数,若定义基类bicycle或motorcar指针,只能访问motorcycle的成员函数。
基类vehicle的Run,Stop函数设为虚函数,编译结果为2.题结果;基类bicycle和motorcar的Run,Stop函数设为虚函数,编译结果如下。因为虚函数只能访问派生类从基类继承的成员。
-
实验6 多态性与虚函数(P285)
2018-05-15 15:16:33实验目的和要求 了解静态联编的动态联编的概念。掌握动态联编的条件。实验内容1.分析并调试下列程序。//sy6_1.cpp#include<iostream>实验目的和要求
了解静态联编的动态联编的概念。掌握动态联编的条件。实验内容1.分析并调试下列程序。//sy6_1.cpp #include<iostream> using namespace std; class Base { public: virtual void f(float x){cout<<"Base::f(float)"<<x<<endl;} void g(float x){cout<<"Base::g(float)"<<x<<endl;} void h(float x){cout<<"Base::h(float)"<<x<<endl;} }; class Derived:public Base { public: virtual void f(float x){cout<<"Derived::f(float}"<<x<<endl;} void g(int x){cout<<"Derived::g(int)"<<x<<endl;} void h(float x){cout<<"Derived::h(float)"<<x<<endl;} }; int main() { Derived d; Base *pb=&d; Derived *pd=&d; pb->f(3.14f);//语句1 pd->f(3.14f);//语句2 pb->g(3.14f);//语句3 pb->h(3.14f);//语句4 pd->h(3.14f);//语句5 return 0; }
(1)找出以上程序中使用了重载和覆盖函数。
答:Base类中函数void g(); 和void h();与Derived类中的函数void g(); 和void h();函数名相同,参数类型不同,构成了函数重载。
(2)写出程序的输出结果,并解释输出结果。程序的输出结果如下:分析:程序首先声明一个基类Base,类中公有成员部分有虚函数f和两个成员函数g、h,接着定义派生类Derived并且继承基类中的公有部分。主函数中先声明一个对象d,接着定义指针pb。然后执行语句1,调用基类中的虚函数,输出第一行结果。接着执行语句2、3、4、5,分别调用派生类中虚函数、基类中的g函数、基类中的h函数,派生类中的h函数,输出第2、3、4、5行结果。2. 分析并调试下列程序//sy6_3.cpp #include<iostream> using namespace std; class Base { public: void f(int x){cout<<"Base::f(int)"<<x<<endl;} void f(float x){cout<<"Base::f(float)"<<x<<endl;} virtual void g(void){cout<<"Base::g(void)"<<endl;} }; class Derived:public Base { public: virtual void g(void){cout<<"Derived::g(void}"<<endl;} }; int main() { Derived d; Base *pb=&d; pb->f(42); pb->f(3.14f); pb->g(); return 0; }
(1)找出以上程序中使用了重载和覆盖函数。答:Base类中函数void f(); 在同一作用域中,函数名相同,参数类型不同,构成了函数重载。
(2)写出程序的输出结果,并解释输出结果。程序的输出结果如下:输出结果解释: pb和pd指向同一地址,它们运行结果应该是相同的,但实际运行出来的结果却不相同,原因是决定pb和pd调用函数运行结果的不是他们指向的地址,而是他们的指针类型。“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。在程序中pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::。
3. 分析并调试下列程序//sy6_3.cpp #include<iostream> using namespace std; class Point { public: Point(double i,double j){x=i;y=j;} double Area(){return 0.0;} private: double x,y; }; class Rectangle:public Point { public: Rectangle(double i,double j,double k,double l):Point(i,j){w=k;h=l;} double Area(){return w*h;} private: double w,h; }; int main() { Point p(3.5,7);//语句1 double A=p.Area();//语句2 cout<<"Area= "<<A<<endl;//语句3 Rectangle r(1.2,3,5,7.8);//语句4 A=r.Area();//语句5 cout<<"Area= "<<A<<endl;//语句6 return 0; }
写出程序的输出结果,并解释输出结果。
程序的输出结果如下:输出结果解释:程序首先执行语句1,创建对象p,调用基类中的构造函数x和y得到初始值3.5和7,接着执行语句2,将p.Area( )赋值给A,因为基类中的Area的返回值为0,因此输出第一行的结果Area=0。然后执行语句4,创建对象r,调用派生类中的构造函数,i,j,w,h得到初始值1.2、3、5、7.8,Area的返回值为w*h=5*7.8=39,输出第二行Area的值为39。4. 分析并调试下列程序//sy6_4.cpp #include<iostream> using namespace std; const double PI=3.1415; class Shap { public: virtual double Area()=0; }; class Triangle:public Shap { public: Triangle(double h,double w){H=h;W=w;} double Area(){return 0.5*H*W;} private: double H,W; }; class Rectangle:public Shap { public: Rectangle(double h,double w){H=h;W=w;} double Area(){return H*W;} private: double H,W; }; class Circle:public Shap { public: Circle(double r){R=r;} double Area(){return PI*R*R;} private: double R; }; class Square:public Shap { public: Square(double s){S=s;} double Area(){return S*S;} private: double S; }; double Total(Shap *s[],int n) { double sum=0; for(int i=0;i<n;i++) sum+=s[i]->Area(); return sum; } int main() { Shap *s[5]; s[0]=new Square(8.0); s[1]=new Rectangle(3.0,8.0); s[2]=new Square(12.0); s[3]=new Circle(8.0); s[4]=new Triangle(5.0,4.0); double sum=Total(s,5); cout<<"SUM = "<<sum<<endl; return 0; }
程序的输出结果如下:(1)指出抽象类。答:抽象类是包含纯虚函数的类,也就是类Shap。
(2)指出纯虚函数,并说明它的作用。答:纯虚函数是virtual double Area()=0;作用是为派生类提供一个一致的接口,最终在派生列中实现了Area(),用于求具体形状的面积。
(3)每个类的作用是什么?整个程序的作用是什么?答:基类是Shap,类Triangle、类Rectangle、类Circle、类Square是Shap的派生类,并且以公有继承的方式继承及Shap,其中类Triangle、类 Rectangle、类Circle、类Square分别是为了计算三角形的面积、矩形的面积、圆的面积、正方形的面积。然后又定义了一个Total函数用来计算各图形的总面积。
5. 某学校对教师每个月工资的计算规定如下:固定工资+课时补贴;教授的固定工资为5000元,每个课时补贴50;副教授的固定工资为3000,每个课时补贴30元;讲师的固定工资为2000元,每个课时补贴20元。定义教师抽象类,派生不同职称的教师类,编写程序求若干个教师的月工资。(sy6_5.cpp)
程序输出结果如下://sy6_5.cpp #include <iostream> using namespace std; class Teacher { public: virtual int Salary()=0; virtual void Print(int)=0; }; class Professor:public Teacher { private: char name[128]; int lessons; public: Professor() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (5000+lessons*50); }; void Print(int money) { cout<<"职称:教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; }; class AssociateProfessor:public Teacher { private: char name[128]; int lessons; public: AssociateProfessor() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (3000+lessons*30); }; void Print(int money) { cout<<"职称:副教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl; }; }; class Lecturer:public Teacher { private: char name[128]; int lessons; public: Lecturer() { cout<<"请输入姓名:"; cin>>name; cout<<"请输入课时:"; cin>>lessons; }; int Salary() { return (2000+lessons*20); }; void Print(int money) { cout<<"职称:讲师 姓名:"<<name<<"薪水:"<<money<<endl<<endl; }; }; int main() { Teacher *t = NULL; int money=0; t = new Professor(); money = t->Salary(); t->Print(money); delete t; t = new AssociateProfessor(); money = t->Salary(); t->Print(money); delete t; t = new Lecturer(); money = t->Salary(); t->Print(money); delete t; t = NULL; return 0; }
6. 把实验5中的第4题的Shape类定义为抽象类,提供共同操作界面的纯虚函数。TwoDimShape类和ThreeDimShape类仍然抽象类,第3层具体类才能提供全部函数的实现。在测试函数中,使用基类指针实现不同派生类对象的操作。、程序编写如下:
程序输出结果如下://sy6_6.cpp #include<iostream> using namespace std; class Shape { public: virtual double area()=0; virtual double bulk()=0; }; class TwoDimShape:public Shape{}; class Circle:public TwoDimShape { public: Circle(double r){R=r;} double area(){return 3.14*R*R;} double bulk(){} private: double R; }; class ThreeDimShape:public Shape{}; class sphere:public ThreeDimShape { public: sphere(double w){R=w;} double area(){} double bulk(){return 4/3*3.14*R*R*R;} private: double R; }; int main() { Shape *s[2]; s[0]=new Circle(3.0); s[1]=new sphere(4.0); cout<<"Area of circle is "<<s[0]->area()<<endl; cout<<"Bulk of sphere is "<<s[1]->bulk()<<endl; return 0; }
分析与讨论1.结合实验内容中第1题和第2题,说明重载与覆盖的区别。答:重载与覆盖的区别:1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。3、覆盖要求参数列表相同;重载要求参数列表不同。4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。
2.总结静态联编和动态联编的区别和动态联编的条件。答:静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。
动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。
动态联编的条件:
①必须把动态联编的行为定义为类的虚函数。
②类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。
③必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。实验总结
通过本次实验我了解静态联编的动态联编的概念以及静态联编和动态联编的区别,了解了什么是重载和覆盖函数掌握动态联编的条件。在同一个作用域内,函数名相同,参数列表不同(参数个数不同,或者参数类型不同,或者参数个数和参数类型都不同),返回值类型可相同也可不同,这种情况叫做C++的重载。而覆盖又被叫做重写,是当在子类中定义一个与父类完全相同的虚函数时,则称子类的这个函数重写了父类的这个虚函数。在之前一直都不明白这两者的概念含义,不知道怎么去运用,通过老师的讲解和实验的操作,大概了解了是怎么一回事,但是还需要在课下花时间去练习。
-
C++实验6_1多态性与虚函数(1)
2018-11-09 22:39:46一、实验目的和要求 1.掌握静态联编和动态联编的概念; 2.掌握成员函数方式运算符重载; 3.掌握友元函数方式运算符重载; 4.理解掌握++、--、=运算符的重载。 二、实验环境(软... -
C程序设计教程与实验指导杨国兴多态性.ppt
2020-04-13 15:12:42C++语言程序设计 杨国兴 张东玲 彭涛 第6章 多态性 6.1 运算符重载 6.2 运算符重载为类的成员函数 6.3 运算符重载为类的友元函数 6.4 虚函数 6.1 运算符重载 6.1.1 问题的提出 例4.3的复数类 #include "iostream.h" ... -
实验四类的继承与多态实验.doc
2020-07-09 22:56:30实验四 类的继承与多态实验1.理解软件重用性的一种形式——继承。 2.能够通过继承已有的类创建新类。 3.理解基类和派生类的概念。 4.能够在派生类中使用构造函数和析构...9.掌握用抽象类和多态性实现编程的方法。 -
第六章实验
2018-06-20 15:15:51实验6 多态性与虚函数(P285)2018年05月15日 15:16:33阅读数:44实验目的和要求 了解静态联编的动态联编的概念。掌握动态联编的条件。实验内容1.分析并调试下列程序。[cpp] view plain copy<span style=... -
广州大学MFC实验报告——学生成绩管理系统
2019-06-08 10:33:38一、实验目的 本实验是对前面所学知识的总结,通过一个比较完整的应用程序的设计,将学过的知识连贯起来,掌握开发一个实际应用程序的步骤,同时...3、虚函数与多态性 4、运算符重载 5、文件的使用 6、算法的使用 三... -
《MATLAB R2016a通信系统建模与仿真28个案例分析》源码
2018-06-04 09:22:068.1信号的突变性与小波变换 8.2信号的突变点检测原理 8.3实验结果与分析 8.3.1Daubechies 5小波用于检测含有突变点的信号 8.3.2Daubechies 6小波用于检测突变点 第9章MIMOOFDM通信系统设计的MATLAB实现 9.1... -
c++ 程序设计
2019-01-20 22:53:37第12章 多态性与虚函数 12.1 多态性的概念 12.2 一个典型的例子 12.3 虚函数 12.3.1 虚函数的作用 12.3.2 静态关联与动态关联 12.3.3 在什么情况下应当声明虚函数 12.3.4 虚析构函数 12.4 纯虚函数与抽象类 12.4.1 ... -
软件工程简答题
2012-01-03 21:26:00多态性的概念,怎样实现多态性? 答:多态性是指相同的操作或函数、过程可作用于多种类型的对象上并获得不同的结果。不同的对象,收到同一消息可以产生不同的结果,这种现象称为多态性。 静多态可以通过模板和函数... -
c++程序设计 谭浩强part1
2010-02-23 20:07:09c++程序设计 谭浩强 本书是谭浩强教授的又一力作,依据C++标准进行介绍,引导学生学习...第12章 多态性与虚函数 第13章 输入输出流 第14章 C++工具 附录A 常用守符与ASCII代码对照表 附录B 运算符号与结合性 参考文献 -
语言程序设计课后习题答案
2012-12-27 17:02:37通过实现继承与多态性,还可以大大提高程序的可重用性,使得软件的开发和维护都更为方便。 面向对象方法所强调的基本原则,就是直接面对客观存在的事物来进行软件开发,将人们在日常生活中习惯的思维方式和表达方式...