在C++語言中有一組基礎的概念一直都容易混淆:Overload、Override和Overwrite分別表示什麼意思?下面把這三個概念整理一下:ios
1. Overload(重載)ide
重載的概念最好理解,在同一個類聲明範圍中,定義了多個名稱徹底相同、參數(類型或者個數)不相同的函數,就稱之爲Overload(重載)。重載的特徵以下:函數
(1)相同的範圍(在同一個類中);
(2)函數名字相同;
(3)參數不一樣;
(4)virtual 關鍵字無關緊要。spa
2. Override(覆蓋)設計
覆蓋的概念實際上是用來實現C++多態性的,即子類從新改寫父類聲明爲virtual的函數。Override(覆蓋)的特徵以下:3d
(1)不一樣的範圍(分別位於派生類與基類);
(2)函數名字相同;
(3)參數列表徹底相同;
(4)基類函數必須有virtual 關鍵字。指針
3. Overwrite(改寫)code
改寫是指派生類的函數屏蔽(或者稱之爲「隱藏」)了與其同名的基類函數。正是這個C++的隱藏規則使得問題的複雜性陡然增長,這裏面分爲兩種狀況討論:對象
(1)若是派生類的函數與基類的函數同名,可是參數不一樣。那麼此時,不論有無virtual關鍵字,基類的函數將被隱藏(注意別與重載混淆)。
(2)若是派生類的函數與基類的函數同名,而且參數也相同,可是基類函數沒有virtual關鍵字。那麼此時,基類的函數被隱藏(注意別與覆蓋混淆)。blog
借鑑一個網上的例子來看Overwrite(改寫)的狀況:
#include <iostream> using namespace std; class Base { public: virtual void f(float x){ cout << "Base::f(float) " << x << endl; } virtual void g(float x){ cout << "Base::g(float) " << x << endl; } void h(float x){ cout << "Base::h(float) " << x << endl; } }; class Derived : public Base { public: virtual void f(float x){ cout << "Derived::f(float) " << x << endl; } virtual void g(int x){ cout << "Derived::g(int) " << x << endl; } void h(float x){ cout << "Derived::h(float) " << x << endl; } }; int main() { Derived d; Base *pb = &d; Derived *pd = &d; // Good : behavior depends solely on type of the object pb->f(3.14f); // Derived::f(float) 3.14 pd->f(3.14f); // Derived::f(float) 3.14 // Bad : behavior depends on type of the pointer pb->g(3.14f); // Base::g(float) 3.14 (surprise!) pd->g(3.14f); // Derived::g(int) 3 // Bad : behavior depends on type of the pointer pb->h(3.14f); // Base::h(float) 3.14 (surprise!) pd->h(3.14f); // Derived::h(float) 3.14 return 0; }
在上面這個例子中:
4. 特殊狀況說明
除了上面講到的三種狀況以外,還有一些比較容易迷惑的地方,例如:
4.1 同名的普通函數與const函數本質上是兩個不一樣的函數,應該等價理解爲這兩個同名函數的參數是不一樣的。在派生類中的virtual函數理解上可能會有誤解。
參見以下例子:
#include <iostream> using namespace std; class Base { public: virtual void f(float x){ cout << "Base::f(float) " << x << endl; } }; class Derived : public Base { public: virtual void f(float x) const { cout << "Derived::f(float) " << x << endl; } }; int main() { Derived d; Base *pb = &d; Derived *pd = &d; // Bad : behavior depends solely on type of the object pb->f(3.14f); // Base::f(float) 3.14 pd->f(3.14f); // Derived::f(float) 3.14 return 0; }
4.2 基類中定義的virtual虛函數,在繼承子類中同名函數自動都屬於虛函數,能夠不須要virtual關鍵字。
4.3 若是基類中定義的函數不是virtual,而子類中又將相同函數定義爲virtual,則稱之爲越位,函數行爲依賴於指針/引用的類型,而不是實際對象的類型。
參見以下例子:
#include<iostream> using namespace std; class Base { public: void f(){ cout << "Base::f() " << endl; } virtual void g(){ cout << "Base::g() " << endl; } }; class Derived : public Base { public: virtual void f(){ cout << "Derived::f() " << endl; } void g(){ cout << "Derived::g() " << endl; } }; class VirtualDerived : virtual public Base { public: void f(){ cout << "VirtualDerived::f() " << endl; } void g(){ cout << "VirtualDerived::g() " << endl; } }; int main() { Base *d = new Derived; Base *vd = new VirtualDerived; d->f(); // Base::f() Bad behavior d->g(); // Derived::g() vd->f(); // Base::f() Bad behavior vd->g(); // VirtualDerived::g() delete d; delete vd; return 0; }
5. 針對非虛函數的繼承說明
在《Effective C++》中講述了這樣一個規則:任何條件下都要禁止從新定義繼承而來的非虛函數。
公有繼承的含義是 "是一個"(is a),"在一個類中聲明一個非虛函數實際上爲這個類創建了一種特殊性上的不變性"。若是將這些分析套用到類B、類D和非虛成員函數B::mf,那麼:
(1)適用於B對象的一切也適用於D對象,由於每一個D的對象「是一個」B的對象。
(2)B的子類必須同時繼承mf的接口和實現,由於mf在B中是非虛函數。
那麼,若是D從新定義了mf,設計中就會產生矛盾。若是D真的須要實現和B不一樣的mf,並且每一個B的對象(不管怎麼特殊)也真的要使用B實現的mf,那麼每一個D將不 "是一個" B。這種狀況下,D不能從B公有繼承。相反,若是D真的必須從B公有繼承,並且D真的須要和B不一樣的mf的實現,那麼,mf就沒有爲B反映出特殊性上的不變性。這種狀況下,mf應該是虛函數。最後,若是每一個D真的 "是一個" B,而且若是mf真的爲B創建了特殊性上的不變性,那麼,D實際上就不須要從新定義mf,也就決不能這樣作。
無論採用上面的哪種論據均可以得出這樣的結論:任何條件下都要禁止從新定義繼承而來的非虛函數。