实验 六 多态性与虚函数

时间:2024-11-24 07:11:33
一、实验目的和要求
    了解静态联编的动态联编的概念。掌握动态联编的条件。
二、实验内容

1.分析并调试下列程序。

  1. //sy6_1.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Base
  5. {
  6. public:
  7. virtual void f(float x){cout<<"Base::f(float)"<<x<<endl;}
  8. void g(float x){cout<<"Base::g(float)"<<x<<endl;}
  9. void h(float x){cout<<"Base::h(float)"<<x<<endl;}
  10. };
  11. class Derived:public Base
  12. {
  13. public:
  14. virtual void f(float x){cout<<"Derived::f(float}"<<x<<endl;}
  15. void g(int x){cout<<"Derived::g(int)"<<x<<endl;}
  16. void h(float x){cout<<"Derived::h(float)"<<x<<endl;}
  17. };
  18. int main()
  19. {
  20. Derived d;
  21. Base *pb=&d;
  22. Derived *pd=&d;
  23. pb->f(3.14f);
  24. pd->f(3.14f);
  25. pb->g(3.14f);
  26. pb->h(3.14f);
  27. pd->h(3.14f);
  28. return 0;
  29. }
(1)找出以上程序中使用了重载和覆盖函数。

(2)写出程序的输出结果,并解释输出结果。

程序运行结果如下:


解释输出结果:

      pb和pd指向同一地址,它们运行结果应该是相同的,但实际运行出来的结果却不相同,原因是决定pb和pd调用函数运行结果的不是他们指向的地址,而是他们的指针类型。“只有在通过基类指针或引用间接指向派生类子类型时多态性才会起作用”。在程序中pb是基类指针,pd是派生类指针,pd的所有函数调用都只是调用自己的函数,和多态性无关,所以pd的所有函数调用的结果都输出Derived::是完全正常的;pb的函数调用如果有virtual则根据多态性调用派生类的,如果没有virtual则是正常的静态函数调用,还是调用基类的,所以有virtual的f函数调用输出Derived::,其它两个没有virtual则还是输出Base::。

2. 分析并调试下列程序.

  1. //sy6_2.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Base
  5. {
  6. public:
  7. void f(int x){cout<<"Base::f(int)"<<x<<endl;}
  8. void f(float x){cout<<"Base::f(float)"<<x<<endl;}
  9. virtual void g(void){cout<<"Base::g(void)"<<endl;}
  10. };
  11. class Derived:public Base
  12. {
  13. public:
  14. virtual void g(void){cout<<"Derived::g(void}"<<endl;}
  15. };
  16. int main()
  17. {
  18. Derived d;
  19. Base *pb=&d;
  20. pb->f(42);
  21. pb->f(3.14f);
  22. pb->g();
  23. return 0;
  24. }
(1)找出以上程序中使用了重载和覆盖函数。

(2)写出程序的输出结果,并解释输出结果。

输出结果如下:


3. 分析并调试下列程序。

  1. //sy6_3.cpp
  2. #include<iostream>
  3. using namespace std;
  4. class Point
  5. {
  6. public:
  7. Point(double i,double j){x=i;y=j;}
  8. double Area(){return 0.0;}
  9. private:
  10. double x,y;
  11. };
  12. class Rectangle:public Point
  13. {
  14. public:
  15. Rectangle(double i,double j,double k,double l):Point(i,j){w=k;h=l;}
  16. double Area(){return w*h;}
  17. private:
  18. double w,h;
  19. };
  20. int main()
  21. {
  22. Point p(3.5,7);
  23. double A=p.Area();
  24. cout<<"Area= "<<A<<endl;
  25. Rectangle r(1.2,3,5,7.8);
  26. A=r.Area();
  27. cout<<"Area= "<<A<<endl;
  28. return 0;
  29. }

写出程序的输出结果,并解释输出结果。

输出结果如下:


4. 分析并调试下列程序。

  1. //sy6_4.cpp
  2. #include<iostream>
  3. using namespace std;
  4. const double PI=3.1415;
  5. class Shap
  6. {
  7. public:
  8. virtual double Area()=0;
  9. };
  10. class Triangle:public Shap
  11. {
  12. public:
  13. Triangle(double h,double w){H=h;W=w;}
  14. double Area(){return 0.5*H*W;}
  15. private:
  16. double H,W;
  17. };
  18. class Rectangle:public Shap
  19. {
  20. public:;
  21. Rectangle(double h,double w){H=h;W=w;}
  22. double Area(){return H*W;}
  23. private:
  24. double H,W;
  25. };
  26. class Circle:public Shap
  27. {
  28. public:
  29. Circle(double r){R=r;}
  30. double Area(){return PI*R*R;}
  31. private:
  32. double R;
  33. };
  34. class Square:public Shap
  35. {
  36. public:
  37. Square(double s){S=s;}
  38. double Area(){return S*S;}
  39. private:
  40. double S;
  41. };
  42. double Total(Shap *s[],int n)
  43. {
  44. double sum=0;
  45. for(int i=0;i<n;i++)
  46. sum+=s[i]->Area();
  47. return sum;
  48. }
  49. int main()
  50. {
  51. Shap *s[5];
  52. s[0]=new Square(8.0);
  53. s[1]=new Rectangle(3.0,8.0);
  54. s[2]=new Square(12.0);
  55. s[3]=new Circle(8.0);
  56. s[4]=new Triangle(5.0,4.0);
  57. double sum=Total(s,5);
  58. cout<<"SUM = "<<sum<<endl;
  59. return 0;
  60. }

运行结果如下:


(1)指出抽象类。
(2)指出纯虚函数,并说明它的作用。
(3)每个类的作用是什么?整个程序的作用是什么?

5. 某学校对教师每个月工资的计算规定如下:固定工资+课时补贴;教授的固定工资为5000元,每个课时补贴50;副教授的固定工资为3000,每个课时补贴30元;讲师的固定工资为2000元,每个课时补贴20元。定义教师抽象类,派生不同职称的教师类,编写程序求若干个教师的月工资。(sy6_5.cpp)

编写程序如下:

  1. //sy6_5.cpp
  2. #include <iostream>
  3. using namespace std;
  4. class Teacher
  5. {
  6. public:
  7. virtual int Salary()=0;
  8. virtual void Print(int)=0;
  9. };
  10. class Professor:public Teacher
  11. {
  12. private:
  13. char name[128];
  14. int lessons;
  15. public:
  16. Professor()
  17. {
  18. cout<<"请输入姓名:";
  19. cin>>name; //字符串中不能有空格
  20. cout<<"请输入课时:";
  21. cin>>lessons; //必须输入数字
  22. };
  23. int Salary()
  24. {
  25. return (5000+lessons*50);
  26. };
  27. void Print(int money)
  28. {
  29. cout<<"职称:教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl;
  30. };
  31. };
  32. class AssociateProfessor:public Teacher
  33. {
  34. private:
  35. char name[128];
  36. int lessons;
  37. public:
  38. AssociateProfessor()
  39. {
  40. cout<<"请输入姓名:";
  41. cin>>name;
  42. cout<<"请输入课时:";
  43. cin>>lessons;
  44. };
  45. int Salary()
  46. {
  47. return (3000+lessons*30);
  48. };
  49. void Print(int money)
  50. {
  51. cout<<"职称:副教授 姓名:"<<name<<" 薪水:"<<money<<endl<<endl;
  52. };
  53. };
  54. class Lecturer:public Teacher
  55. {
  56. private:
  57. char name[128];
  58. int lessons;
  59. public:
  60. Lecturer()
  61. {
  62. cout<<"请输入姓名:";
  63. cin>>name;
  64. cout<<"请输入课时:";
  65. cin>>lessons;
  66. };
  67. int Salary()
  68. {
  69. return (2000+lessons*20);
  70. };
  71. void Print(int money)
  72. {
  73. cout<<"职称:讲师 姓名:"<<name<<"薪水:"<<money<<endl<<endl;
  74. };
  75. };
  76. int main()
  77. {
  78. Teacher *t = NULL;
  79. int money=0;
  80. //教授
  81. t = new Professor();
  82. money = t->Salary();
  83. t->Print(money);
  84. delete t;
  85. //副教授
  86. t = new AssociateProfessor();
  87. money = t->Salary();
  88. t->Print(money);
  89. delete t;
  90. //讲师
  91. t = new Lecturer();
  92. money = t->Salary();
  93. t->Print(money);
  94. delete t;
  95. t = NULL;
  96. return 0;
  97. }

程序运行结果如下:


6. 把实验5中的第4题的Shape类定义为抽象类,提供共同操作界面的纯虚函数。TwoDimShape类和ThreeDimShape类仍然抽象类,第3层具体类才能提供全部函数的实现。在测试函数中,使用基类指针实现不同派生类对象的操作。

三、分析与讨论

1.结合实验内容中第1题和第2题,说明重载与覆盖的区别。                                                                                                     答:重载与覆盖的区别:1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系。3、覆盖要求参数列表相同;重载要求参数列表不同。4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的。

2.总结静态联编和动态联编的区别和动态联编的条件。                                                                                                               答:静态联编是指联编工作在编译阶段完成的,这种联编过程是在程序运行之前完成的,又称为早期联编。要实现静态联编,在编译阶段就必须确定程序中的操作调用(如函数调用)与执行该操作代码间的关系,确定这种关系称为束定,在编译时的束定称为静态束定。静态联编对函数的选择是基于指向对象的指针或者引用的类型。其优点是效率高,但灵活性差。

           动态联编是指联编在程序运行时动态地进行,根据当时的情况来确定调用哪个同名函数,实际上是在运行时虚函数的实现。这种联编又称为晚期联编,或动态束定。动态联编对成员函数的选择是基于对象的类型,针对不同的对象类型将做出不同的编译结果。C++中一般情况下的联编是静态联编,但是当涉及到多态性和虚函数时应该使用动态联编。动态联编的优点是灵活性强,但效率低。                                                                                                                                                                      动态联编的条件:                                                                                                                                                           ①必须把动态联编的行为定义为类的虚函数。                                                                                                                     ②类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来。                                                                           ③必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。

四、实验小结

      通过本次实验,我们主要要了解静态联编的动态联编的概念以及掌握动态联编的条件。静态联编是指在程序编译连接阶段进行的联编,而动态联编是在程序运行时进行的联编。动态联编的条件是:必须把动态联编的行为定义为类的虚函数;类之间应满足子类型关系,通常表现为一个类从另一个类公有派生而来;必须先使用基类指针指向子类型的对象,然后直接或者间接使用基类指针调用虚函数。