今天將焦點放在針對 Class(類別) 設計的新關鍵字。這些關鍵字在「寫出意義明確的程式碼」以及「避免低級錯誤」扮演關鍵要角。
首先,來看看 final
。若用於類別的宣告上,明白指出「我這個類別不許別人再給我繼承下去我就是要絕子絕孫」,其寫法如下:
struct Base {
virtual ~Base();
};
struct Lonely final : public Base {
~Lonely();
};
struct Love : public Lonely { // Error, class Lonely is final
virtual ~Love();
};
上述 Lonely
類別的設計者將其宣告為 final
,不讓人繼承自她。有很多理由這麼做,其中一個較為常見的是,Lonely
的內部實作沒有考慮到被繼承後的行為,為了避免繼承後產生「副作用」,乾脆直接宣告不給繼承。
那麼,若要基於 Lonely
的功能來擴充新的功能,該怎麼做?小明真聰明,懂得問這個好問題。要達到這個目的,可以改用組合(Composition),也就是讓新的類別把 Lonely
當做成員變數,呼叫其提供的服務來實作擴充功能。
final
也可用於成員函數,寫法如下:
struct Base {
virtual void Shit();
virtual void NoShit();
};
struct Lonely : public Base {
void Shit() final;
void NoShit();
};
struct Love : public Lonely
void Shit(); // Error because Shit() is final
void NoShit();
};
下一個關鍵字是 override
,同樣用於類別繼承。這是個可以降低低級錯誤發生率的發明,很貼心。
struct Base {
virtual void Shit();
};
struct Lonely : public Base {
virtual void Shut() override; // Error, incorrect function name.
};
手腦不一致,人常有錯字。上例中,Lonely
很明顯是要實作 Shit
這個函數,但手殘打錯字。以往這類錯誤不容易發現,提高了除錯成本。加上 override
後,編譯直接報錯。
上例中的不是大便很容易看被穿,實務上,有很多讓人摸不著頭緒的錯誤,就是會讓你遇到。
接下來是幾個相似功能的關鍵字,以下列表示:
struct Lonely {
Lonely() = default;
~Lonely() = default;
Lonely(const Lonely& rhs) = delete;
Lonely& oeprator=(const Lonely& rhs) = delete;
};
C++11 以前的編譯器本來就會在某些情況下幫你生成「建構式」以及「解構式」的實作碼,但規則總讓不熟悉 C++ 的人處處感受驚喜。有了 default
,明著要編譯幫忙實作,要是自己雞婆也寫一個,那就報錯。
delete
則是明著說,這幾個特殊函式(Copy Ctor, Assignment Operator)不給呼叫,語義上就是「這個類別不能被複製」。以往要達到這個目的,會把那些不給用的特殊函式放到 private
段。
Modern C++ 講求程式碼的可讀性,透過這些新的關鍵字,該禁止的明白禁止,要編譯器幫忙的就明白要求,藉此寫出「不容易被誤用的類別」,這就是現代化 C++。