繼承模式和存取權限有什麼關係?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繼承。
(三)六個特殊函式
雖然六個特殊函數都不繼承,但基礎類別特殊函式可被衍生類別成員函式使用,當然衍生類別也會有自己專屬的特殊函式。因此在定義六個特殊函數時,也可以重複使用基礎類別的特殊函數,如果要在body中使用基礎類別特殊函數時,一定要使用base::特殊函數,不然衍生類別會以為特殊函數是自己的,而陷入無限遞迴的窘境,或是找不到這些特殊函數。
大部分的特殊函式都必須自己呼叫基礎類別特殊函式,只有無參數的構建子和解構子會自動呼叫,不過建構子建議還是要自己呼叫,因為多數建構子會帶有參數而解構子不允許帶有參數,故解構子一定會自動呼叫。
衍生類別可以直接使用基礎類別建構子來做基礎類別的初始化,如果是在初始化列呼叫基礎類別建構子,就不需要用::指出scope;而如果在本體中呼叫基礎類別建構子,就需要用::指出scope。其他特殊的函數建構,也是一樣可以重複使用基礎類別的特殊函數。
要注意建構子和解構子的呼叫順序是相反的,建構子會先執行基礎再執行衍生,而解構子會先執行衍生再執行基礎。不過通常比較少客製化解構子,因為我們會避免自己管理記憶體,也就是我們會避免使用到new/delete。
留言
張貼留言