一,類繼承
定義:從已有的類派生出新的類,而派生類繼承了原有類的特征,包括方法。
目标:提供可重用的代碼
二,一個簡單的基類
<strong><span style="font-size:18px;">#include <iostream>
#include <cstring>
using namespace std;
class student //基類
{
private:
char name[20];
int num;
int age;
public:
student(const char *m_name,const int m_num,const int m_age);
~student();
virtual void display();//派生類有重載 故采用虛函數形式
};
class graduate:public student //派生類 注意寫法
char major[20];
int thesis;
graduate(const char *m_major,const int m_thesis,student &st);
~graduate();
void display();
void setThesis(int i);
student::student(const char *m_name,const int m_num,const int m_age)
strcpy(name,m_name);
num=m_num;
age=m_age;
}
student::~student()
cout<<"student is over"<<endl;
void student::display()
cout<<"name:"<<name<<"\nnum:"<<num<<"\nage"<<age<<endl;
graduate::graduate(const char* m_major, const int m_thesis, student& st):student(st)
{ //特别注意:由于派生類不能直接使用基類私有成員,是以必須使用基類構造函數
st.display();
strcpy(major,m_major);
thesis=m_thesis;
graduate::~graduate()
cout<<"graduate is over"<<endl;
void graduate::display()
cout<<"major:"<<major<<"\nthesis:"<<thesis<<endl;
void graduate::setThesis(int i)
thesis=i;
int main()
student st("tianshuai",1,18);
cout<<"**************\n";
graduate gd("compuater",5,st);
gd.display();
return 0;
</span></strong>輸出:<strong><span style="font-size:18px;">name:tianshuainum:1
age18
**************
name:tianshuai
num:1
major:compuater
thesis:5
graduate is over
student is over
</span></strong>
【簡介】1)繼承類 繼承了基類的私有成員,但是不能通過繼承類的對象直接通路私有成員
2)派生類不能直接通路基類私有成員,派生類構造函數必須使用基類構造函數
3)派生類構造函數特點:基類對象首先被建立 建立graduate gd(); student 對象先被建立
建立派生類對象時,先調用基類構造函數,再調用派生類構造函數
派生類對象過期時,先析構派生類,再析構基類
4)不能講基類對象和位址賦給派生類
student st;
graduate &gd=st; // Not Allow
graduate *gd=st; // Not Allow
如果允許的話,則基類可以通路派生類成員,但通路基類沒有的成員,對于基類對象來說是沒有意義的
st.setThesis(int i) //因為基類沒有該方法
三,繼承----- is-a 關系
繼承方式:共有繼承、私有繼承、保護繼承
共有繼承建立一種is-a關系:派生類對象也是一個基類對象,可以對基類對象執行的任何操作,也可以對派生類對象執行
例如:Fruit 是水果類,有重量和熱量。Banana是派生類,包含重量和熱量外,還添加專門香蕉成員,這些成員通常不用于Fruit.
四,多态共有繼承
兩種實作機制:1>在派生類中重新定義基類方法。2>使用虛方法
不存在虛拟構造函數,但基類的虛拟析構函數是必要的。請看下例:<strong><span style="font-size:18px;">class ClxBase{public:
ClxBase(){cout << "Output from the con ClxBase!" << endl;};
virtual ~ClxBase(){cout << "Output from the destructor of class ClxBase!" << endl;};
virtual void DoSomething(){ cout << "Do something in class ClxBase!" << endl; };
class ClxDerived : public ClxBase{
ClxDerived() {cout << "Output from the con ClxDerived!" << endl;};
~ClxDerived() { cout << "Output from the destructor of class ClxDerived!" << endl; };
void DoSomething() { cout << "Do something in class ClxDerived!" << endl; };
void main()
<span style="color:#ff0000;">ClxBase</span> *pTest = new <span style="color:#ff0000;">ClxDerived</span>; //基類的指針 引用派生類對象
pTest->DoSomething(); //調用派生類的方法
delete pTest;
}</span></strong>
輸出:Do something in class ClxDerived!
Output from the destructor of class ClxDerived!
這個很簡單,非常好了解。
但是,如果把類ClxBase析構函數前的virtual去掉,那輸出結果就是下面的樣子了:
Do something in class ClxDerived!
也就是說,類ClxDerived的析構函數根本沒有被調用!一般情況下類的析構函數裡面都是釋放記憶體資源,而析構函數不被調用的話就會造成記憶體洩漏。我想所有的C++程式員都知道這樣的危險性。當然,如果在析構函數中做了其他工作的話,那你的所有努力也都是白費力氣。
當然,并不是要把所有類的析構函數都寫成虛函數。因為當類裡面有虛函數的時候,編譯器會給類添加一個虛函數表,裡面來存放虛函數指針,這樣就會增加類的存儲空間。是以,隻有當一個類被用來作為基類的時候,才把析構函數寫成虛函數。
五,靜态聯編和動态聯編
函數名聯編:将源代碼中函數調用解釋為執行特定的函數代碼塊
但是在C++中由于函數重載,編譯器必須檢視函數參數以及函數名才能确定使用哪個函數
靜态聯編:在編譯過程中進行聯編
但是虛函數使這項工作變得更困難,使用哪一個函數不能在編譯時确定
動态聯編:程式運作時選擇正确虛方法的代碼
1)為什麼有兩種類型聯編,為什麼 靜态聯編為預設
動态聯編讓您能夠重新定義類方法,而靜态聯編則很差,但靜态聯編效率高。
如果派生類不重新定義基類任何方法,則不需要動态聯編。僅僅将那些派生類需要重新定義的函數定義為虛拟的
2)虛函數工作原理
編譯器給每個對象添加一個隐藏成員,其中儲存一個該函數位址的指針,這種數組成為虛函數表。
調用虛函數時,檢視存儲在對象中虛拟函數表位址,然後轉向相應函數位址表,如果使用類聲明中定義的第一個虛函數,則程式将使用數組中的第一個函數位址,并執行具有該位址的函數。
3)注意事項
如果重新定義繼承的方法,則應確定與原來的原型完全相同。
如果基類聲明被重載了,則應在派生類中重新定義所有基類版本
六,抽象基類ABC(abstract base class)
說明:某幾個子類的所公有的資料和方法抽象出來組成一個抽象基類,然後從抽象基類中派生出這幾個子類,可以通過基類指針數組同時管理這幾個子類。對于每個子類中的不同方法,可以将該方法在抽象基類中定義為純虛函數的方式,同時在各子類中将該方法定義為虛函數。
#include <iostream>using namespace std;
const double pi=3.14;
class BaseEllipse //抽象基類 含有圓跟橢圓的公共成員
double x;
double y;
BaseEllipse(double t_x=0,double t_y=0)
{
x=t_x;
y=t_y;
}
virtual ~BaseEllipse(){}
void Move(int nx,int ny)
x=nx;
y=ny;
cout<<"中心坐标為:"<<"("<<x<<","<<y<<")"<<endl;
<strong><span style="font-size:18px;"></span></strong><pre name="code" ><span style="color:#ff0000;">virtual void Area()const=0;//純虛函數結尾處為 “=0” 在類中可以隻定義,不用實作</span>};class Circle:public BaseEllipse //圓{private: double r;public: Circle(double t_x=0,double
t_y=0,double t_r=0); //半徑和中心坐标 Circle(const BaseEllipse & ba,double t_r=0); void Area()const;};class Ellipse:public BaseEllipse //橢圓{private: double a; double b;public: Ellipse(double t_x=0,double t_y=0,double t_a=0,double t_b=0); Ellipse(const BaseEllipse
& p,double t_a=0,double t_b=0); void Area()const;};/*圓*/Circle::Circle(double t_x,double t_y,double t_r):BaseEllipse(t_x,t_y){ r=t_r;}Circle::Circle(const BaseEllipse & ba,double t_r):BaseEllipse(ba){ r=t_r;}void Circle::Area()const{ cout<<pi*r*r<<endl;}/*橢圓*/Ellipse::Ellipse(double
t_x,double t_y,double t_a,double t_b):BaseEllipse(t_x,t_y){ a=t_a; b=t_b;}Ellipse::Ellipse(const BaseEllipse & ba,double t_a,double t_b):BaseEllipse(ba){ a=t_a; b=t_b;}void Ellipse::Area()const{ cout<<0.5*a*b<<endl;}int main(){ Circle c1(0,0,5); c1.Move(1,2);
c1.Area(); Ellipse e1(0,0,7,8); e1.Move(3,4); e1.Area(); return 0;}