1、繼承與派生
繼承是使代碼可以復用的重要手段,也是面向對象程序設計的核心思想之一。簡單的說,繼承是指一個對象直接使用另一對象的屬性和方法。繼承呈現了 面向對象程序設 計的層次結構, 體現了 由簡單到復雜的認知過程。C++中的繼承關系就好比現實生活中的父子關系,繼承一筆財產比白手起家要容易得多,原始類稱為基類,繼承類稱為派生類,它們是類似於父親和兒子的關系,所以也分別叫父類和子類。而子類又可以當成父類,被另外的類繼承。繼承的方式有三種分別為公有繼承(public),保護繼承(protect),私有繼承(private)。
定義格式如下:
2、繼承方式及訪問屬性
(1) 公有繼承(public)
公有繼承的特點是基類的公有成員和保護成員作為派生類的成員時,它們都保持原有的狀態,而基類的私有成員對派生類是不可見的,也不能被這個派生類的子類所訪問。
(2)私有繼承(private)
私有繼承的特點是基類的公有成員和保護成員都作為派生類的私有成員,並且不能被這個派生類的子類所訪問。
(3)保護繼承(protected)
保護繼承的特點是基類的所有公有成員和保護成員都成為派生類的保護成員,並且只能被它的派生類成員函數或友元訪問,基類的私有成員仍然私有,且對派生類不可見。
private能夠對外部和子類保密,即除了成員所在的類本身可以訪問之外,別的都不能直接訪問。protected能夠對外部保密,但允許子類直接訪問這些成員。public、private和protected對成員數據或成員函數的保護程度可以用下表來描述:舉個栗子:
1 class Base //父類 2 { 3 private: 4 int _priB; 5 protected: 6 int _proB; 7 public: 8 int _pubB; 9 } ; 10 class Derived: public Base //子類,繼承自base,繼承類型為公有繼承 11 { 12 private: 13 int _d_pri; 14 protected: 15 int _d_pro; 16 public: 17 void funct() 18 { 19 int d; 20 d=_priB; //error:基類中私有成員在派生類中是不可見的 21 d=_proB; //ok: 基類的保護成員在派生類中為保護成員 22 d=_pubB; //ok: 基類的公共成員在派生類中為公共成員 23 } 24 int _d_pub; 25 } ;
總結:(1). public繼承是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用, 因為每個子類對象也都是一個父類對象。
26 class C :private Base //基類Base的派生類C(私有繼承) 27 { 28 public: 29 void funct() 30 { 31 int c; 32 c=_priB; //error:基類中私有成員在派生類中是不可見的 33 c=_proB; //ok:基類的保護成員在派生類中為私有成員 34 c=_pubB; //ok:基類的公共成員在派生類中為私有成員 35 } 36 }; 37 class E :protected Base //基類Base的派生類E(保護繼承) 38 { 39 public: 40 void funct() 41 { 42 int e ; 43 e=_priB; //error:基類中私有成員在派生類中是不可見的 44 e=_proB; //ok:基類的保護成員在派生類中為保護成員 45 e=_pubB; //ok:基類的公共成員在派生類中為保護成員 46 } 47 };
總結:
(2). 基類的private成員 在派生類中是不能被訪問的, 如果基類成員 不想在類外直接被訪問, 但需要 在派生類中能訪問, 就定義為protected。 可以看出保護成員 限定符是因繼承才出現的。
(3). protected/private繼承是一個實現繼承, 基類的部分成員 並非完全成為子類接口 的一部分, 是 has-a 的關系原則, 所以非特殊情況下不會使用這兩種繼承關系, 在絕大多數的場景下使用的 都是公有繼承。 私有繼承以為這is-implemented-in-terms-of(是根據……實現的) 。 通常比組合(composition) 更低級, 但當一個派生類需要訪問 基類保護成員 或需要重定義基類的虛函數時它就是合理的。
49 int main() 50 { 51 int a; 53 D d; 54 a=D._priB; //error:公有繼承基類中私有成員在派生類中是不可見的,對對象不可見 55 a=D._proB; //error:公有繼承基類的保護成員在派生類中為保護成員,對對象不可見 56 a=D._pubB; //ok:公有繼承基類的公共成員在派生類中為公共成員,對對象可見 58 C c; 59 a=c._priB; //error:私有繼承基類中私有成員在派生類中是不可見的, 對對象不可見 60 a=c._proB; //error:私有繼承基類的保護成員在派生類中為私有成員,對對象不可見 61 a=c._pubB; //error:私有繼承基類的公共成員在派生類中為私有成員,對對象不可見 63 E e; 64 a=e._priB; //error:保護繼承基類中私有成員在派生類中是不可見的, 對對象不可見 65 a=e._proB; //error:保護繼承基類的保護成員在派生類中為保護成員,對對象不可見 66 a=e._pubB; //error:保護繼承基類的公共成員在派生類中為保護成員,對對象不可見 67 68 return 0; 69 }
(4)不管是哪種繼承方式, 在派生類內部都可以訪問基類的公有成員和保護成員 , 基類的私有成員存在但是在子類中不可見( 不能訪問) 。
(5)使用關鍵字class時默認的繼承方式是private, 使用struct時默認的繼承方式是public, 不過最好顯式的寫出繼承方式。
(6)在實際運用中一般使用都是public繼承, 極少場景下才會使用protetced/private繼承。
在struct繼承中,如果沒有顯式給出繼承類型,則默認的為public繼承;在class繼承中,如果沒有顯式給出繼承類型,則默認的為private繼承;
繼承關系&訪問限定符
注意:派生類需要添加自己的構造函數(下一篇會詳細分析這些特性)
(1)創建派生類對象時,程序首先創建基類對象;
(2)要通過初始化表用基類的構造函數給繼承過來的成員變量提供初始值;
(3)如果省略了基類的初始化表,程序會使用基類的默認構造函數;
(4)也應初始化派生類新增的數據成員;
(5)派生類對象過期時,首先調用派生類的析構,再調用基類的析構;
(6)派生類應根據需要添加自己的成員變量和方法;
3、基類與派生類的關系-指針或引用
(1)基類指針或引用可以在不進行顯式類型轉換的情況下指向或引用派生類對象,但是基類指針或引用只能調用基類的方法,無法調用派生類的方法,而派生類指針或引用是不能指向或引用基類對象的;
(2)參數為基類引用或指針的函數,也可以將派生類對象作為參數,這可以讓基類對象使用派生類來初始化;
(3)這也可以將派生類賦值給基類 – 此時派生類的基類部分被復制給基類,將派生類對象賦值給基類對象,但這只涉及到基類的部分,派生類中的派生部分將被忽略掉,其實質是調用基類的賦值運算符 – 此函數參數為基類的引用;
(4)公有繼承建立一種is-a 關系 – is-a-kind-of
即派生類是基類的一種 – 只是比基類更特殊了一下而已,並非 is-like-a – 即沒有比喻關系,派生類只能在基類的基礎上添加屬性,但是不能刪除屬性,並非 is-implenmented-as-a – 即沒有實現關系 如數組可以用來實現棧,但是不能從Array派生出Stack類,而是將數組對象封裝在棧里並非use-a – 即沒有使用關系 – 可以通過友元或類來實現類之間的通信(使用)。public繼承是一個接口繼承,保持is-a原則,每個父類可用的成員對子類也可用,因為每個子類對象也都是一個父類對象。比如白馬是馬,香蕉是水果。
(5)protetced/private繼承建立一種has-a 關系