C++ — 靜態繫結與動態繫結

Dawn_sf發表於2017-10-10

 靜態繫結與動態繫結

——————————————————————————————————



靜態繫結,動態繫結算是一個比較偏的知識點,這個也是我在Effective C++這本書當中學習到的. 我覺得很有必要寫一篇部落格記.

首先我們要知道靜態型別和動態型別的概念:

靜態型別:就是它在程式中被宣告時所採用的型別(或理解為型別指標或引用的字面型別),在編譯期確定;

動態型別:目前所指物件的型別。是在執行期決定的。物件的動態型別可以更改,但是靜態型別無法更改。

接下來我引用Effective C++這本書中的例子,幫助理解:

class Shape
{
public:
	enum shapeColor{Red,Green,Blue};
	//所有形狀都必須提供一個函式,同來繪出自己.
	virtual void draw(shapeColor color = Red) const = 0;
	...
};
class Rectangle : public Shape
{
public:
	//注意,賦予不同的預設引數值,這真糟糕
	virtual void draw(shapeColor color = Green) const;
};
class Circle : public Shape
{
public:

	virtual void draw(shapeColor color) const;
	//請注意,以上這麼寫則當客戶以物件呼叫此函式,一定要指定引數值.
	//因為靜態繫結下這個函式並不從其base繼承預設引數值
	//但若以指標(或reference)呼叫此函式,可以不指定引數值
	//因為動態繫結下這個函式會從其base繼承預設引數值
	......

};
Shape *ps;         // ps的靜態型別為Shape*,它沒有動態型別,因為它尚未指向任何物件;  
Shape *pc = new Circle;      // pc的靜態型別為Shape*,它的動態型別為Circle*;  
Shape *pr = new Rectangle;   // pr的靜態型別為Shape*,它的動態型別為Rectangle*;  



// 動態型別可以在程式執行過程中改變(通常是經由賦值動作):  
ps = pc;                     // ps的動態型別如今是Circle*;  
ps = pr;                     // ps的動態型別如今是Rectangle*;  



現在我們來理解何為靜態繫結和動態繫結???

靜態繫結:又名前期繫結(eraly binding),繫結的是靜態型別,所對應的函式或屬性依賴於物件的靜態型別,發生在編譯期;

動態繫結:又名後期繫結(late binding),繫結的是動態型別,所對應的函式或屬性依賴於物件的動態型別,發生在執行期;


比如常見的,virtual函式是動態繫結,non-virtual函式是靜態繫結,預設引數值也是靜態繫結.Virtual函式系動態繫結而來,

思是呼叫了一個virtual函式時,究竟呼叫那一份函式實現程式碼,取決於付出呼叫的那個物件的動態型別:

pc->draw(shape::Red); //呼叫Circle::draw(shape::Red)
pr->draw(shape::Red); //呼叫Rectangle::draw(shape::Red)

其實這些都是老掉牙的知識點,但是今天我們帶來了virtual函式不同的地方,預設值! virtual函式是動態繫結的,而預設引數

為靜態繫結. 意思是你可能會在"呼叫一個定義與derived class內的virtual函式"的同時,卻使用base class為他所指定的預設

數值.

pr->draw();        //呼叫Rectangle::draw(shape::Red)!

此例當中,pr的動態型別為Rectangle*,所以呼叫的是Rectangle的virtual函式,一如你所預料.Rectangle::draw函式的預設值應

該是Green,但由於pr的靜態型別為shape*,所以此一呼叫的預設引數值來自shape class而非Rectangle class!結局是這個函式調

用有這奇怪並且幾乎沒有人預料得到的組合,由shape class和Rectangle class的draw生命式各處一份力.以上事實不只侷限於

"ps,pc和pr都是指標"的情況;即使把指標換成references問題依然存在.重點在於draw是一個virtual函式,而它有個預設引數值在

derived class中被重新定義了.


為什麼C++堅持以這種乖張的方式來運作呢? 答案在於執行期效率. 如果預設引數值為動態繫結,編譯器就必須有某種方法在執行

期為virtual函式決定適當的引數預設值.這比目前實行的"在編譯器決定"的機制更慢而且更復雜.為了程式的執行速度和編譯器實

現上的簡易度,C++做了這樣的取捨,其結果就是你如今所享受的執行效率. 但是如果你重新定義了繼承而來的預設引數值,而且偏

偏在預設引數值上面出現bug,那麼你可能需要一個通宵都不一定調的出來.

相關文章