把 C++ 想象成人類社會。
訪問許可權、繼承、友元將無比真實的反應人類社會中的種種關係。
一、類內部訪問許可權
1 2 3 4 5 6 7 8 9 |
+---------+ |class | --> 人 +---------+ |public | --> 你能幹啥 |protected| --> 你留下啥 |private | --> 你藏了啥 +---------+ |friend | --> 你的圈子 +---------+ |
幾乎所有人都知道你能幹啥,這某種程度上是你在這個社會的價值體現。但這些不相干的人,並不知道你留下了啥,是萬貫家財?還是詩書傳承?誰知道?你的家人知道(類內部),你的後代知道(子類),你的朋友知道(友元)。至於你藏了些啥,除了你的家人(類內部),可能也只有幾位密友(友元)瞭解。
二、繼承時訪問許可權
1 2 3 4 5 6 7 |
+---------+ |Inherits | --> 繁衍後代 +---------+ |public | --> 嫡出 |protected| --> 庶出 |private | --> 私生 +---------+ |
C++ 的社會沒有計劃生育,也似乎沒有限制一夫一妻。所以就存在正房和偏房的問題,嫡長子光明正大(public)的繼承了你的財產和光環(public, protected)。其餘庶出(protected)的就沒那麼好運了,僅僅能夠分到一點財產(protected, protected)。而風流快活的產物——私生子(private)更是可憐,分到的東西誰也說不得,是私密。(private,private)。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
class Father { public: int x; protected: int y; private: int z; }; class FirstSon : public Father { // x is public // y is protected // z is not accessible from Father }; class SecondSon : protected Father { // x is protected // y is protected // z is not accessible from Father }; class Illegitimate : private A { // x is private // y is private // z is not accessible from Father }; |
三、開始處理各種繼承問題(兩種訪問許可權的混搭)
1 2 3 4 5 6 7 8 9 10 |
class Base { protected: int prot_mem; }; class Sneaky : public Base { friend void clobber(Sneaky &s) { s.j = s.prot_mem = 0; } friend void clobber(Base &b) { b.prot_mem = 0; } // error int j; }; |
prot_mem
是 Base
的 protected
成員。那麼對於 以 public
的姿勢繼承 Base
的子類 Sneaky
來說,它可以取得該成員(嫡長子)。
而第一個 clobber
作為 Sneaky
的密友,自然也可以取得 Sneaky
的成員,如其自己的 j
,以及繼承自父類的 prot_mem
。
第二個 clobber
作為 Sneaky
的密友,卻妄想去直接拿其父親留下的 prot_mem
,這顯然是不合理的。 注意這裡與上面那個的區別,該密友越過了 Sneaky
,直接去拿其父親的遺物,這是違背了社會法規的。
再來看三代同堂:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
// 父 class Base { public: int pub_mem; protected: int prot_mem; private: int pri_mem; }; // 子 struct Pub_Derv : public Base { int f() { return prot_mem; } int g() { return pri_mem; } // error }; struct Priv_Derv : private Base { int f1() const { return prot_mem; } }; // 孫 struct Derived_from_Public : public Pub_Derv { int use_base() { return prot_mem; } }; struct Derived_from_Private : public Priv_Derv { int use_base() { return prot_mem; } // error }; |
- 注意第一個 error 處,是由於嫡長子去觸及父親藏起來的東西導致的。
- 再看第二個 error 處,是由於私生子的兒子想去拿爺爺留下的東西導致的。
細分析,兒子輩,一個嫡長子一個私生子,prot_mem
是祖輩留下之物,雖然兩個兒子都繼承了,但對於嫡長子來說,prot_mem
仍舊是可以遺留之物(protected
許可權);而對於私生子來說,prot_mem
卻成了需藏起來之物(private
許可權)。那麼,到了孫子輩,長房長孫繼續拿到祖輩繼承之物,而反觀私房長孫,卻和祖輩幾乎毫無瓜葛了。
再來看一個朋友亂入的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
class Base { friend class Pal; protected: int prot_mem; }; class Sneaky : public Base { int j; }; class Pal { public: int f(Base b) { return b.prot_mem; } int f2(Sneaky s) { return s.j; } // error int f3(Sneaky s) { return s.prot_mem; } }; class D2 : public Pal { public: int mem(Base b) { return b.prot_mem; } // error }; |
Pal
是父親的密友,拿到父親所留之物理所當然。然而直接跑去拿兒子的私物,卻不合情理。但如果只是拿兒子所繼承的父親遺留之物呢?這是可能的,在很多情況下,也是合情合理的。(可能所留本來就有密友的份)。
好了,如果父親的密友也有了嫡長子,他去拿該父親的所留之物呢?這就有點不講規矩了,密友關係僅存在於父輩兩者之間,繼承者無論如何也無法去拿上一輩朋友的東西的。
四、私生子的逆襲
從上面的一些例子,可以很明顯的看到私生子的慘狀,只要私生,祖上的一切接變成 private
,幾乎沒法再傳承下去。
幸好 C++ 的社會裡倒也公平,提供了一個 using
關鍵字,讓私生子也有了逆襲的機會。如下例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#include <cstddef> class Base { public: std::size_t size() const { return n; } protected: std::size_t n; }; class Derived : private Base { public: using Base::size; protected: using Base::n; }; |
所謂私生子的 Derived
,原本 size
和 n
都是私有成員,經過 using
宣告後,前者為 public
,後者為 protected
。逆襲成功。
以上皆是對 C++ 物件導向體系中,類內部及繼承時成員可見度規則的一些調侃。
祝願那些還在這個社會體系中混的程式設計師們,讓自己的家族開枝散葉,生生不息~
(更高階的齊家之道,估計還要學習設計模式才好。)