在C++學習中,尤其在涉及類這一內容時,我們往往會遇到這樣一個問題:如何設計一個類,要求該類不能被繼承?
最簡單的方法就是將該類的建構函式聲明為私人方法,但是這又帶來另一個弊端:那就是該類本身不能產生對象了,當然這樣能夠滿足該類不能被繼承的要求,卻得不償失。下面介紹一種比較巧妙的方法來供大家參考,也算是自己學習中的一個小小的總結吧。
主要思想就是:通過一個建構函式和解構函式都是私人的輔助類來實現的。假設不想被繼承的類為A,我們可以將A聲明為輔助類的一個友元,另外,為了讓A的子類B能夠調用輔助類的建構函式,對於A則虛繼承輔助類。說起來有點麻煩,下面直接上碼:
1 #include <iostream> 2 using namespace std; 3 class A; 4 class Assistant 5 { 6 private:
7 friend A;
8 Assistant(){}; 9 ~Assistant(){};10 };11 12 class A : public virtual Assistant13 {14 public:15 A(){};16 ~A(){};17 };18 19 class B : public A20 {21 public:22 B(){};23 ~B(){};24 };25 26 int main(int argc, char* argv[])27 {28 A a; // 可以構造29 B b; // 不能構造30 return 0;31 }
這樣的話就實現了A的衍生類別不能繼承A類的一個設計,但是每次聲明一個非繼承類都要在Assistant類中添加一個友元,這樣很不方便,而且一旦非繼承的類多了,Assistant類就會看起來很臃腫,這時我們就可以採用泛型程式設計中的模板技術:
1 #include <iostream> 2 using namespace std; 3 4 template <class T> 5 class Assistant 6 { 7 private: 8 friend T;
9 Assistant(){};10 ~Assistant(){};11 };12 13 class A : public virtual Assistant <A>14 {15 public:16 A(){};17 ~A(){};18 };19 20 class B : public A21 {22 public:23 B(){}:24 ~B(){};25 };26 27 int main(int argc, char* argv[])28 {29 A a; // 可以構造30 B b; // 不能構造31 return 0;32 }
這樣的話,就能夠實現多個不被繼承的類,公用同一個輔助類Assistant,而無需在Assistant類中添加任何多餘的聲明。任何類想設計成不被繼承,只需虛繼承Assistant類即可。
到現在為止,可能有些人對虛繼承(virtual)還不是很瞭解,為什麼這裡A類一定要虛繼承Assistant類呢?
虛繼承的功能是:當出現了菱形繼承體系的時候,子孫類不會繼承多個原始祖先類。這裡有什麼用呢?這會導致基類的初始化任務必須由繼承體系中最底層的類完成,即B類聲明對象時,必須直接調用Assistant類中的建構函式,而不是先調用A類的建構函式,然後再通過A類來調用Assistant類的建構函式,如果是這樣的話,那麼上面B類聲明對象b時,是可以構造的。所以A類必須虛繼承Assistant而不是簡單的public繼承。為了進一步解釋這段說明,我們看看下面這段代碼:
1 #include <iostream> 2 using namaspace std; 3 4 template <class T> 5 class Assistant 6 { 7 private: 8 friend T;
9 Assistant(){};10 ~Assistant(){};11 };12 13 class A : public Assistant <A>14 {15 public:16 A(){};17 ~A(){};18 };19 20 class B : public A21 {22 public:23 B(){};24 ~B(){};25 };26 27 int main(int argc, char* argv[])28 {29 A a; // 可以構造30 B b; // 可以構造!31 }
所以,當A直接public繼承Assistant,而不是virtual public繼承Assistant時,A類還是可以被B類繼承的,因為這個時候B確實是可以成功的聲明對象。
具體的原因就是我上面說到的:由於A不是virtual public繼承Assistant的,所以B在聲明對象時,是通過A類來調用祖先類Assistant的建構函式的,而A類是Assistant的友元類,故可以調用被私人化的Assistant()方法,所以此時,B b;可以成功構造!