1. 程式人生 > >實驗六 多型性與虛擬函式

實驗六 多型性與虛擬函式

實驗目的和要求

瞭解靜態聯編的動態聯編的概念。  掌握動態聯編的條件。

實驗內容

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)寫出程式的輸出結果,並解釋輸出結果。

程式的輸出結果如下:


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}"<<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);
    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
{
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++中一般情況下的聯編是靜態聯編,但是當涉及到多型性和虛擬函式時應該使用動態聯編。動態聯編的優點是靈活性強,但效率低。

       動態聯編的條件:必須把動態聯編的行為定義為類的虛擬函式;類之間應滿足子型別關係,通常表現為一個類從另一個類公有派生而來;必須先使用基類指標指向子型別的物件,然後直接或者間接使用基類指標呼叫虛擬函式。

實驗總結

  通過本次的學習和實踐,我學到了很多知識,比如瞭解靜態聯編的動態聯編的概念並掌握動態聯編的條件;與此同時我還總結出了靜態聯編和動態聯編的區別以及過載與覆蓋的區別。通過這些知識的學習與總結讓我對於本課程的學習又掌握了一些不同的知識點,擴大了自己學習的範圍,為自己及下一步的學習也有了很大的幫助。