C++中定義一個不能被繼承的類(友元類+類模板)
自從C++11
標準出來之後,就有了關鍵字final
可以直接宣告一個類不能被繼承。那麼,在此之前如果想要一個類不能被繼承,可能還需要下一番功夫。
文章目錄
原文地址:https://www.qingdujun.com/zh-CN/cpp-class-cannot-be-inherited.html
1.宣告建構函式為私有
如果將建構函式、虛構函式宣告為私有的,那麼這個類肯定不能被繼承。
class A {
private:
A() {}
~A() {}
};
存在的問題
雖然這樣的類不能被子類繼承,但是本身也無法建立物件。為了解決該問題,可以使用借鑑單例模式的方式,在類中建立好物件。
由於這裡講的不是單例模式,也不追究這個例子是否符合多執行緒訪問了。
class A {
public:
static A* getInstance() {
if (!s_inst) {
s_inst = new A();
}
return s_inst;
}
private:
A() {}
~A() {}
static A* s_inst;
};
A* A::s_inst = nullptr;
並不符合要求
這種被限制了的類,顯然不是我們想要的。那麼如果再改進一下,以滿足要求呢?
2.子類宣告為基類的友元類
上面很明確的說明了,建構函式為私有的情況下是無法被繼承的。但是有一種方法,可以用“友元類”來打破它。
class B;//Class declaration
class A {
private:
A() {}
~A() {}
friend B;
};
class B : public A {
public:
B() {}
~B() {}
};
以上這種寫法有一個問題,就是我們改變了“基類A”讓它可以被繼承。
但是也引入了另一個問題——其他類也可以繼承“類B”了。
而且我們的目的是寫一個不能被繼承的類,那還有沒有其他的方法呢?
3.虛繼承——子類直接呼叫虛基類的建構函式(私有)
既然基類建構函式為私有的,肯定不能直接繼承,這一點是確定的。上一節中,既然“類B”可以被繼承,因為它的建構函式是公有的。
那麼還有一種方式是“虛繼承”。
我們知道“虛繼承”有一個特性,一般而言,初始化時子類都會呼叫直接基類的建構函式從而先初始化基類,但是C++“多繼承”時為了避免重複初始化問題,引入了“虛繼承”概念————它使用了一套,讓最遠的子類直接初始化虛基類,其父類不再初始化基類,而避免了重複初始化問題。
class B;//Class declaration
class A {
private:
A() {}
~A() {}
friend B;
};
class B : virtual public A {
public:
B() {}
~B() {}
};
class C : public B { //error
public:
C() {
}
};
對上面的程式碼解釋一下,由於“類B”虛繼承自“類A”,而“類C”又繼承自“類B”。
那麼就會由“類C”直接呼叫“類A”的建構函式,這一步就失敗了…從而造成了“類B”和“類A”這一個整體類不能被繼承。
那麼,“類C”是否可以“虛繼承”自上面這個整體類呢?
答案也是否定的,“類B”之所以可以虛繼承“類A”————那是因為聲明瞭“友元類”。
很顯然,就算“類C”虛繼承以上的任意一個類,都是不可以的,因為不管繼承誰,都會由“類C”自己負責呼叫“類A”的建構函式,這一步始終無法做到。
4.類模板——不失一般性
為了不失一般性,這裡引入“類模板”概念。引入該概念之後,“類B”繼承“類A”的時候一定要指明型別(比如,A<B>
)。
template <typename T>
class A {
friend T;
private:
A() {}
~A() {}
};
class B : virtual public A<B> {
};
那麼,這裡最終的“類B”是一個不能被繼承的類。但是注意,“類A”卻變的是可以被繼承的。
2018-12-7 北京 海淀
References:
[1] C++ Primer(第五版)
[2] C++中定義一個不能被繼承的類
[3] 面試題28:不能被繼承的類
[4] c++設計一個不能被繼承的類,為什麼必須是虛繼承?原因分析
[5] C++ 類模板和模板類