在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 Assistant 13 { 14 public: 15 A(){}; 16 ~A(){}; 17 }; 18 19 class B : public A 20 { 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 A 21 { 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 A 21 { 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;可以成功構造!