[筆記]繼承模式與存取權限|C++

繼承模式和存取權限有什麼關係?private繼承不是讓所有存取權限變成private嗎?為什麼衍生類別依舊可以存取基礎類別?類別開發者和使用者有什麼區分?特殊函式的繼承會和一般函式一樣嗎?

還是很困惑嗎?文章裡有答案喔~😎

一、繼承 (inheritance)

(一)存取權限

public, protected, private的存取權限:(*號表示也是非成員函式)

 

成員函式

夥伴函式

非成員函式(使用者)

Public 成員

O

O*

O*

Protected成員

O

O*

X

Private成員

O

O*

X

成員函式可以『直接』存取自己的所有成員,也就是使用this指標;但非成員要『間接』存取他類的成員,夥伴函式 (friend function)也是非成員函數,故也要間接存取。非成員函式必須先實例化他類物件,且透過這個物件去存取此物件內的成員,在尚未實例化前,是不可能存取任何成員的,除非是static物件。

重要的資料建議不要宣告為protected,因為public / protected繼承的衍生類別 (derived class)的成員函式可以直接使用基礎類別 (base class)的protected資料。除非基礎類別和衍生類別關係非常密切,不然建議用public的set或get函式間接存取這些重要資料。

(二)繼承模式與存取權限

如果不寫繼承模式,則class預設為private;struct預設為public,但實務上建議還是寫清楚繼承模式。

class derivedMulti : public base1, base2 //非常危險的寫法,因為這裡的base2是private!盡量寫public base1, private base2

基礎類別成員權限 繼承模式

public 繼承

protected繼承

private繼承

public 權限

public

protected

private

protected權限

protected

protected

private

private權限

private

private

private

對於衍生類別來說,如果是public繼承,基礎類別的權限不變;protected繼承,只將public權限改為protected,其餘不變;而private繼承,基礎類別的權限全部都變成private。

注意:繼承型態是限制衍生類別使用者的存取權限,而非衍生類別開發者的存取權限!

在繼承中,需要釐清類別使用者與類別開發者,因為繼承的權限限制和使用行為有差。類別開發者是指使用this指標存取者,因此在類別標頭檔 (如:.h檔)和實作檔 (如:.cpp檔)中類別定義內的部分,都可以使用this指標。如果不使用this指標存取,而是用實例化的物件存取,就都視為類別使用者故在類別定義外或其他檔案 (如:main.cpp)的部分,一定是類別使用者,但在類別定義中的存取行為,也可以是用物件存取的使用者行為。

繼承型態與存取權限關係圖

在private繼承中,直接繼承的衍生類別對上存取權限不變,隔代繼承的衍生類別無法隔代存取,而物件也無法對上存取,因此private繼承無法隔代繼承。在protected繼承中,直接繼承的類別對上存取權限不變,隔代繼承的類別隔代存取權限不變,而物件對上無法存取,因此protected繼承可以隔代繼承。在public繼承中,不影響任何類別或物件的存取權限,因此public繼承可以隔代繼承,也不改變存取權限。

 

直接繼承

隔代繼承

物件對上存取

Private 繼承

O

X

X

Protected 繼承

O

O

X

Public 繼承

O

O

O

如果不想要隔代繼承且不想要讓使用者使用有對上的存取權限,則可以使用private繼承;如果想隔代繼承且不想要讓使用者使用有對上的存取權限,則可以使用protected繼承;如果想隔代繼承且想要讓使用者使用有對上的存取權限,則可以使用public繼承。

(三)六個特殊函式

如果不知道六個特殊函式,請先看這篇文章的簡介:[筆記]類別、特殊函式、內嵌函式、函式物件|C++

雖然六個特殊函數都不繼承,但基礎類別特殊函式可被衍生類別成員函式使用,當然衍生類別也會有自己專屬的特殊函式。因此在定義六個特殊函數時,也可以重複使用基礎類別的特殊函數,如果要在body中使用基礎類別特殊函數時,一定要使用base::特殊函數,不然衍生類別會以為特殊函數是自己的,而陷入無限遞迴的窘境,或是找不到這些特殊函數。

大部分的特殊函式都必須自己呼叫基礎類別特殊函式,只有無參數的構建子和解構子會自動呼叫,不過建構子建議還是要自己呼叫,因為多數建構子會帶有參數而解構子不允許帶有參數,故解構子一定會自動呼叫。

衍生類別可以直接使用基礎類別建構子來做基礎類別的初始化,如果是在初始化列呼叫基礎類別建構子,就不需要用::指出scope;而如果在本體中呼叫基礎類別建構子,就需要用::指出scope。其他特殊的函數建構,也是一樣可以重複使用基礎類別的特殊函數。

要注意建構子和解構子的呼叫順序是相反的,建構子會先執行基礎再執行衍生,而解構子會先執行衍生再執行基礎。不過通常比較少客製化解構子,因為我們會避免自己管理記憶體,也就是我們會避免使用到new/delete。

二、相關文章

留言