分析:這是Adobe 公司2007 年校園招聘的最新筆試題。這道題除了考察應聘者的C++ 基本功底外,還能考察反應能力,是一道很好的題目。
在Java 中定義了關鍵字final ,被final 修飾的類不能被繼承。但在C++ 中沒有final 這個關鍵字,要實現這個要求還是需要花費一些精力。
首先想到的是在C++ 中,子類的構造函數會自動調用父類的構造函數。同樣,子類的析構函數也會自動調用父類的析構函數。要想一個類不能被繼承,我們只要把它的構造函數和析構函數都定義為私有函數。那么當一個類試圖從它那繼承的時候,必然會由于試圖調用構造函數、析構函數而導致編譯錯誤。
可是這個類的構造函數和析構函數都是私有函數了,我們怎樣才能得到該類的實例呢?這難不倒我們,我們可以通過定義靜態來創建和釋放類的實例。基于這個思路,我們可以寫出如下的代碼:
///////////////////////////////////////////////////////////////////////
// Define a class which can't be derived from
///////////////////////////////////////////////////////////////////////
class FinalClass1
{
public :
static FinalClass1* GetInstance()
{
return new FinalClass1;
}
static void DeleteInstance( FinalClass1* pInstance)
{
delete pInstance;
pInstance = 0;
}
private :
FinalClass1() {}
~FinalClass1() {}
};
這個類是不能被繼承,但在總覺得它和一般的類有些不一樣,使用起來也有點不方便。比如,我們只能得到位于堆上的實例,而得不到位于棧上實例。
能不能實現一個和一般類除了不能被繼承之外其他用法都一樣的類呢?辦法總是有的,不過需要一些技巧。請看如下代碼:
///////////////////////////////////////////////////////////////////////
// Define a class which can't be derived from
///////////////////////////////////////////////////////////////////////
template <typename T> class MakeFinal
{
friend T;
private :
MakeFinal() {}
~MakeFinal() {}
};
class FinalClass2 : virtual public MakeFinal<FinalClass2>
{
public :
FinalClass2() {}
~FinalClass2() {}
};
這個類使用起來和一般的類沒有區別,可以在棧上、也可以在堆上創建實例。盡管類 MakeFinal <FinalClass2> 的構造函數和析構函數都是私有的,但由于類 FinalClass2 是它的友元函數,因此在 FinalClass2 中調用 MakeFinal <FinalClass2> 的構造函數和析構函數都不會造成編譯錯誤。
但當我們試圖從 FinalClass2 繼承一個類并創建它的實例時,卻不同通過編譯。
class Try : public FinalClass2
{
public :
Try() {}
~Try() {}
};
Try temp;
由于類 FinalClass2 是從類 MakeFinal <FinalClass2> 虛繼承過來的,在調用 Try 的構造函數的時候,會直接跳過 FinalClass2 而直接調用 MakeFinal<FinalClass2> 的構造函數。非常遺憾的是, Try 不是 MakeFinal <FinalClass2> 的友元,因此不能調用其私有的構造函數。
基于上面的分析,試圖從 FinalClass2 繼承的類,一旦實例化,都會導致編譯錯誤,因此是 FinalClass2 不能被繼承。這就滿足了我們設計要求。