總結:不要重新定義乙個繼承而來的預設引數值,因為
預設引數值是靜態繫結
,而virtual函式(你唯一應該覆寫的東西)是動態繫結。
我們應該知道,virtual函式是動態繫結(dynamically bound),預設引數值卻是靜態繫結(statically bound)。
物件的靜態型別(static type)是它在程式中被宣告時採用的型別,例如
class shape;
virtual void draw(shapecolor color=red) const=0;
……};class rectangle: public shape;
class circle: public shape;
這個繼承很簡單。現在這樣使用
shape* ps;
shape* pc=new circle;
shape* pr=new rectangle;
這些指標型別都是pointer-to-shape型別,都是靜態型別shape*。物件的動態型別是指「目前所指物件型別」。動態型別可以表現出乙個物件將會有什麼行為。pc動態型別是circle*,pr動態型別是rectangle*,ps沒有動態型別(它沒有指向任何物件)。動態型別可以在執行過程中改變,重新賦值可以改變動態型別。
virtual函式是動態繫結的,呼叫哪乙份函式實現的**,取決於呼叫的那個物件的動態型別。
pc->draw(shape::red);
pr->draw(shape::red);
這樣呼叫無可非議,都帶有引數值。但是如果不帶引數值呢:
pr->draw();//呼叫rectangle::draw(shape::red)
上面呼叫中,pr動態型別是rectangle*,所以呼叫rectangle的virtual函式。rectangle::draw函式預設值是green,但是pr是靜態型別shape*,所以這個
呼叫的預設引數值來自shape class
,不是rectangle class。這次呼叫兩個函式各出了一半的力。
c++之所以使用這麼怪異的運作方式,是因為效率問題。如果預設引數值動態繫結,編譯器必須有某種辦法在執行期為virtual函式決定適當的引數預設值。這比目前實行的「在編譯器決定」的機制更慢且更複雜。為了執行速度和編譯器實現上的簡易度,c++做了這樣的取捨。
我們嘗試遵守這個規則,給base class和derived class提供相同引數值:
class shape;
virtual void draw(shapecolor color=red) const=0;
……};class rectangle: public shape;
這樣問題又來了,**重複且帶著相依性(with dependencies):如果shape內預設引數值改變了,那麼derived classes的預設引數值也要改變,否則就會導致重複定義乙個繼承而來的預設引數值。
當時如果的確需要derived classes的預設引數值,那麼就需要替代方法。
條款35列出了一些virtual函式的替代方法,例如nvi手法【以下未完成】
條款37 絕不重新定義繼承而來的預設引數值
條款37 絕不重新定義繼承而來的預設引數值 靜態繫結就是在程式中被宣告時所採用的型別 includeusing namespace std class shape virtual void draw shapecolor color red const 0 class rectangle publi...
條款37 絕不重新定義繼承而來的預設引數值
乙個用於描述幾何形狀的class class shape 所有形狀都必須提供乙個函式,繪出自己 virtual void draw shapecolor color red const 0 class rectangle public shape class circle public shape ...
條款37 決不要重新定義繼承而來的非虛函式
class b class d public b 甚至對b,d或mf一無所知,也可以定義乙個型別d的物件x,d x x是型別d的乙個物件 那麼,如果發現這麼做 b pb x 得到x的指標 pb mf 通過指標呼叫mf 和下面這麼做的執行行為不一樣 d pd x 得到x的指標 pd mf 通過指標呼叫...