繼承性是面向對象程序設計的第二大特性,它允許在既有類的基礎上創建新類,新類可以繼承既有類的數據成員和成員函數,可以添加自己特有的數據成員和成員函數,還可以對既有類中的成員函數重新定義。利用類的繼承和派生實現了更高層次的代碼可重用性,符合現代軟件開發的思想。
C++語言同時支持單一繼承和多重繼承。單一繼承是指派生類只從一個基類繼承而來;相應的,多重繼承指派生類同時從兩個或更多的基類繼承而來。java只支持單一繼承。
一. 派生類
派生類的定義格式如下:
class <派生類名>:[繼承方式]<基類名1>
[,[繼承方式]<基類名2>,...,[繼承方式]<基類名n>]
{
<派生類新增的數據成員和成員函數定義>
};
說明:
(1)定義派生類關鍵字可以是class或者是struct,兩者區別是:用class定義派生類,默認的繼承方式是private,用struct定義派生類,默認的繼承方式為public。新增加的成員默認屬性也是class對應private屬性,struct對應public屬性。
(2)基類不能被派生類繼承的兩類函數是構造函數和析構函數。
二. 3種繼承方式下基類成員在派生類中的訪問屬性
用下面的代碼簡單理解一下:
1 #include "stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 class Base 6 { 7 private: 8 int priData; 9 protected: 10 int proData; 11 public: 12 int pubData; 13 }; 14 15 class D1:private Base//私有繼承 16 { 17 void f1() 18 { 19 //priData=1;//基類private成員在派生類中不可直接訪問 20 proData=2;//基類的protected成員在派生類中為private訪問屬性 21 pubData=3;//基類的public成員在派生類中為private訪問屬性 22 } 23 }; 24 25 class D2:protected Base//保護繼承 26 { 27 void f2() 28 { 29 //priData=1;//基類private成員在派生類中不可直接訪問 30 proData=2;//基類的protected成員在派生類中為protected訪問屬性 31 pubData=3;//基類的public成員在派生類中為protected訪問屬性 32 } 33 }; 34 35 class D3:public Base//公有繼承 36 { 37 void f3() 38 { 39 //priData=1;//基類private成員在派生類中不可直接訪問 40 proData=2;//基類的protected成員在派生類中為protected訪問屬性 41 pubData=3;//基類的public成員在派生類中為public訪問屬性 42 } 43 }; 44 45 int main() 46 { 47 Base obj; 48 //obj.priData=1;//對象不可訪問Base類中private成員 49 //obj.proData=2;//對象不可訪問Base類中protected成員 50 obj.pubData=3; 51 D1 objD1; 52 //objD1.pubData=3;//private屬性,不可訪問 53 D2 objD2; 54 //objD2.pubData=3;//protected屬性,不可訪問 55 D3 objD3; 56 objD3.pubData=3;//public屬性,可以訪問 57 return 0; 58 }
基類的private成員函數雖然在派生類的成員函數中不可直接訪問,但派生類的成員函數可以通過調用基類被繼承的函數來間接訪問這些成員。如果基類的函數被繼承后在派生類中仍為public成員,則可以通過派生類對象直接調用。
先來看一下類成員的訪問屬性及作用:
訪問屬性 | 作用 |
private | 只允許該類的成員函數及友元函數訪問,不能被其他函數訪問 |
protected | 既允許該類的成員函數及友元函數訪問,也允許其派生類的成員函數訪問 |
public | 既允許該類的成員函數訪問,也允許類外部的其他函數訪問 |
好了,繼續通過代碼來理解:
1 #include "stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 class Base 6 { 7 private: 8 int priData; 9 protected: 10 int proData; 11 public: 12 int pubData; 13 //在類的定義中不能對數據成員進行初始化 14 void SetData()//為基類中的數據成員賦值 15 { 16 priData=100; 17 proData=200; 18 pubData=300; 19 } 20 void Print() 21 { 22 cout<<"priData="<<priData<<endl; 23 cout<<"proData="<<proData<<endl; 24 cout<<"pubData="<<pubData<<endl; 25 } 26 }; 27 28 class Derived:public Base 29 { 30 public: 31 void ChangeData() 32 { 33 SetData(); 34 proData=12;//在派生類的成員函數類可以訪問基類的非私有成員 35 } 36 }; 37 38 int main() 39 { 40 Base b; 41 b.SetData(); 42 b.Print(); 43 44 Derived d1; 45 d1.ChangeData(); 46 d1.pubData=13; 47 d1.Print(); 48 49 return 0; 50 }
程序運行結果如下:
三. 派生類的構造函數和析構函數
在定義一個派生類的對象時,在派生類中新增加的數據成員當然用派生類的構造函數初始化,但是對於從基類繼承來的數據成員的初始化工作就必須由基類的構造函數完成,這就需要在派生類的構造函數中完成對基類構造函數的調用。同樣,派生類的析構函數值能完成派生類中新增加數據成員的掃尾、清理工作,而從基類繼承來的數據成員的掃尾工作也應有基類的析構函數完成。由於析構函數不能帶參數,因此派生類的析構函數默認直接調用了基類的析構函數。
派生類構造函數定義格式如下:
<派生類名>(<總形式參數表>):<基類名1>(<參數表1>),
<基類名2>(<參數表2>),[...,<基類名n>(<參數表n>),其他初始化項>]
{
[<派生類自身數據成員的初始化>]
}
說明:
(1)總形式表給出派生類構造函數中所有的形式參數,作為調用基類帶參構造函數的實際參數以及初始化本類數據成員的參數;
(2)一般情況下,基類名后面的參數表中的實際參數來自前面派生類構造函數形式參數總表,當然也可能是與前面形式參數無關的常量;
(3)在多層次繼承中,每一個派生類只需要負責向直接基類的構造函數提供參數;如果一個基類有多個派生類,則每個派生類都要負責向該積累的構造函數提供參數。
1.單一繼承
1 #include"stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 class Other 6 { 7 public: 8 Other() 9 { 10 cout<<"constructing Other class"<<endl; 11 } 12 ~Other() 13 { 14 cout<<"destructing Other class"<<endl; 15 } 16 }; 17 18 class Base 19 { 20 public: 21 Base() 22 { 23 cout<<"constructing Base class"<<endl; 24 } 25 ~Base() 26 { 27 cout<<"destructing Base class"<<endl; 28 } 29 }; 30 31 class Derive:public Base 32 { 33 private: 34 Other ot; 35 public: 36 Derive() 37 { 38 cout<<"constructing Derive class"<<endl; 39 } 40 ~Derive() 41 { 42 cout<<"destructing Derive class"<<endl; 43 } 44 }; 45 46 int main() 47 { 48 Derive d; 49 50 return 0; 51 }
程序運行結果如下:
可以看到定義派生類對象時,構造函數的調用順序:
a.先調用基類的構造函數
b.然后調用派生類對象成員所屬類的構造函數(如果有對象成員)
c.最后調用派生類的構造函數
析構函數的調用順序正好與構造函數調用順序相反。
2.多重繼承
1 #include"stdafx.h" 2 #include<iostream> 3 using namespace std; 4 5 class Grand 6 { 7 int g; 8 public: 9 Grand(int n):g(n) 10 { 11 cout<<"Constructor of class Grand g="<<g<<endl; 12 } 13 ~Grand() 14 { 15 cout<<"Destructor of class Grand"<<endl; 16 } 17 }; 18 19 class Father:public Grand 20 { 21 int f; 22 public: 23 Father(int n1,int n2):Grand(n2),f(n1) 24 { 25 cout<<"Constructor of class Father f="<<f<<endl; 26 } 27 ~Father() 28 { 29 cout<<"Destructor of class Father"<<endl; 30 } 31 }; 32 33 class Mother 34 { 35 int m; 36 public: 37 Mother(int n):m(n) 38 { 39 cout<<"Constructor of class Mother m="<<m<<endl; 40 } 41 ~Mother() 42 { 43 cout<<"Destructor of class Mother"<<endl; 44 } 45 }; 46 47 class Son:public Father,public Mother 48 { 49 int s; 50 public: 51 Son(int n1,int n2,int n3,int n4):Mother(n2),Father(n3,n4),s(n1) 52 { 53 cout<<"Constructor of class Son s="<<s<<endl; 54 } 55 ~Son() 56 { 57 cout<<"Destructor of class Son"<<endl; 58 } 59 }; 60 61 int main() 62 { 63 Son s(1,2,3,4); 64 return 0; 65 }
程序運行結果如下:
可以看到,與單一繼承不同的是:在多重繼承中,派生類有多個平行的基類,這些處於同一層次的基類構造函數的調用順序,取決於聲明派生類時所指定的各個基類的順序,而與派生類構造函數的成員初始化列表中調用基類構造函數的順序無關。