實驗目的和要求
//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();函數名相同,參數類型不同,構成了函數重載。
//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;
}
答:Base類中函數void f(); 在同一作用域中,函數名相同,參數類型不同,構成了函數重載。
輸出結果解釋: pb和pd指向同一地址,它們運行結果應該是相同的,但實際運行出來的結果卻不相同,原因是決定pb和pd調用函數運行結果的不是他們指向的地址,而是他們的指針類型。“只有在通過基類指針或引用間接指向派生類子類型時多態性纔會起作用”。在程序中pb是基類指針,pd是派生類指針,pd的所有函數調用都只是調用自己的函數,和多態性無關,所以pd的所有函數調用的結果都輸出Derived::是完全正常的;pb的函數調用如果有virtual則根據多態性調用派生類的,如果沒有virtual則是正常的靜態函數調用,還是調用基類的,所以有virtual的f函數調用輸出Derived::,其它兩個沒有virtual則還是輸出Base::。
//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;
}
寫出程序的輸出結果,並解釋輸出結果。
程序的輸出結果如下://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;
}
答:抽象類是包含純虛函數的類,也就是類Shap。
答:純虛函數是virtual double Area()=0;作用是爲派生類提供一個一致的接口,最終在派生列中實現了Area(),用於求具體形狀的面積。
答:基類是Shap,類Triangle、類Rectangle、類Circle、類Square是Shap的派生類,並且以公有繼承的方式繼承及Shap,其中類Triangle、類 Rectangle、類Circle、類Square分別是爲了計算三角形的面積、矩形的面積、圓的面積、正方形的面積。然後又定義了一個Total函數用來計算各圖形的總面積。
//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;
}
程序輸出結果如下://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、方法的覆蓋是子類和父類之間的關係,是垂直關係;方法的重載是同一個類中方法之間的關係,是水平關係2、覆蓋只能由一個方法,或只能由一對方法產生關係;方法的重載是多個方法之間的關係。3、覆蓋要求參數列表相同;重載要求參數列表不同。4、覆蓋關係中,調用那個方法體,是根據對象的類型(對象對應存儲空間類型)來決定;重載關係,是根據調用時的實參表與形參表來選擇方法體的。
答:靜態聯編是指聯編工作在編譯階段完成的,這種聯編過程是在程序運行之前完成的,又稱爲早期聯編。要實現靜態聯編,在編譯階段就必須確定程序中的操作調用(如函數調用)與執行該操作代碼間的關係,確定這種關係稱爲束定,在編譯時的束定稱爲靜態束定。靜態聯編對函數的選擇是基於指向對象的指針或者引用的類型。其優點是效率高,但靈活性差。
動態聯編是指聯編在程序運行時動態地進行,根據當時的情況來確定調用哪個同名函數,實際上是在運行時虛函數的實現。這種聯編又稱爲晚期聯編,或動態束定。動態聯編對成員函數的選擇是基於對象的類型,針對不同的對象類型將做出不同的編譯結果。C++中一般情況下的聯編是靜態聯編,但是當涉及到多態性和虛函數時應該使用動態聯編。動態聯編的優點是靈活性強,但效率低。
動態聯編的條件:
①必須把動態聯編的行爲定義爲類的虛函數。
②類之間應滿足子類型關係,通常表現爲一個類從另一個類公有派生而來。
③必須先使用基類指針指向子類型的對象,然後直接或者間接使用基類指針調用虛函數。
實驗總結
通過本次實驗我瞭解靜態聯編的動態聯編的概念以及靜態聯編和動態聯編的區別,瞭解了什麼是重載和覆蓋函數掌握動態聯編的條件。在同一個作用域內,函數名相同,參數列表不同(參數個數不同,或者參數類型不同,或者參數個數和參數類型都不同),返回值類型可相同也可不同,這種情況叫做C++的重載。而覆蓋又被叫做重寫,是當在子類中定義一個與父類完全相同的虛函數時,則稱子類的這個函數重寫了父類的這個虛函數。在之前一直都不明白這兩者的概念含義,不知道怎麼去運用,通過老師的講解和實驗的操作,大概瞭解了是怎麼一回事,但是還需要在課下花時間去練習。