場景
在傳統C++中,經常容易發現意外重載虛函數的事情:
struct Base {
virtual void foo();
};
struct SubClass: Base {
void foo();
};
有下列三種場景:
-
SubClass::foo
可能是程序員加入的一個和基類虛函數恰好同名的成員函數,卻被編譯器當作重載虛函數 -
SubClass::foo
可能是程序員想重載虛函數,但是因爲形參列表不同導致編譯器認爲這是一個新定義的成員函數 - 當基類的虛函數
Base::foo
被刪除後,SubClass::foo
就不再重載該虛函數而搖身一變成爲一個普通的成員函數
override
一旦類中的某個函數被聲明爲虛函數,那麼在所有的派生類中它都是虛函數。一個派生類的函數如果覆蓋了某個繼承而來的虛函數,那麼它的形參類型必須與基類函數完全一致。
派生類中如果定義了一個函數與基類中虛函數的名字相同但是形參列表不同,編譯器會認爲新定義的函數與基類中原有的函數是相互獨立的。這會帶來一個問題:如果我們本來希望派生類可以覆蓋掉基類中的虛函數,但是一不小心把形參列表寫錯了,這可能與我們的本意不符。
C++11新標準提供了override
關鍵字來顯式地告知虛擬器進行重載,編譯器將檢查基類是否存在這樣的虛函數,否則將無法通過編譯。這樣的好處是使得程序員的意圖更加清晰(覆蓋基類中的虛函數),如果我們使用override
關鍵字標記了某個函數但是該函數沒有覆蓋已有的虛函數,此時編譯器會報錯。
struct Base {
virtual void foo(int);
};
struct SubClass: Base {
virtual void foo(int) override; // 合法
virtual void foo(float) override; // 非法, 父類無此虛函數
};
final
我們可以把類中的某個函數指定爲final
,之後任何嘗試覆蓋該函數的操作都會引發錯誤,用於防止類被繼續繼承或者終止虛函數繼續重載。
struct Base {
virtual void foo() final;
};
struct SubClass1 final: Base {
}; // 合法
struct SubClass2: SubClass1 {
}; // 非法, SubClass1已final
struct SubClass3: Base {
void foo(); // 非法, foo已final
};
Reference
[1] C++ Primer
[2] 現代C++教程