條款37 絕不要重新定義繼承而來的預設引數值

2021-07-04 02:40:36 字數 1666 閱讀 5176

總結:不要重新定義乙個繼承而來的預設引數值,因為

預設引數值是靜態繫結

,而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 通過指標呼叫...