動態類型與靜態類型
靜態類型
是指不需要考慮表達式的執行期語義,僅分析程序文本而決定的表達式類型。靜態類型僅依賴於包含表達式的程序文本的形式,而在程序運行時不會改變。通俗的講,就是上下文無關,在編譯時就可以確定其類型。
動態類型
是指由一個左值表達式表示的左值所引用的最終派生對象的類型。例:如果一個靜態類型爲“類 B ”的指針p 指向一個繼承於 B的類 D 的對象,則表達式 *p 的動態類型爲“D”。引用按照相似規則處理。一般地講,基類的指針和基類引用有可能爲動態類型,就是說在運行之前不能夠確定其真實類型。通常我們說,“基類指針指向的對象的實際/真正類型”或“基類引用所引用的對象的實際/真正類型”,就是它們的動態類型。很顯然,這個動態類型是 C++ 語言通過指針和引用實現運行時多態能力的核心概念。
動態綁定與靜態綁定
靜態綁定:編譯時綁定,通過對象調用
動態綁定:運行時綁定,通過地址實現
只有採用“指針->函數()”或“引用變量.函數()”的方式調用C++類中的虛函數纔會執行動態綁定。對於C++中的非虛函數,因爲其不具備動態綁定的特徵,所以不管採用什麼樣的方式調用,都不會執行動態綁定。
即所謂動態綁定,就是基類的指針或引用有可能指向不同的派生類對象,對於非虛函數,執行時實際調用該函數的對象類型即爲該指針或引用的靜態類型(基類類型);而對於虛函數,執行時實際調用該函數的對象類型爲該指針或引用所指對象的實際類型。比如下面代碼:
class Base {
public:
void Print() {
cout << "Print() from Base." << endl;
}
virtual void Display() {
cout << "Display() from Base." << endl;
}
};
class Derived1 : public Base {
public:
void Print() {
cout << "Print() from Derived1." << endl;
}
void Display() {
cout << "Display() from Derived2." << endl;
}
};
class Derived2 : public Base {
public:
void Print() {
cout << "Print() from Derived2." << endl;
}
void Display() {
cout << "Display() from Derived2." << endl;
}
};
class Derived3 : public Base {
public:
void Print() {
cout << "Print() from Derived3." << endl;
}
void Display() {
cout << "Display() from Derived3." << endl;
}
};
由運行結果可以看到,b是一個基類指針,它指向了一個派生類對象,基類Base裏面有兩個函數,其中test爲虛函數,func爲非虛函數。因此,對於test就表現爲動態綁定,實際調用的是派生類對象中的test,而func爲非虛函數,因此它表現爲靜態綁定,也就是說指針類型是什麼,就會調用該類型相應的函數。
虛函數、動態綁定、運行時多態之間的關係
簡單地說,虛函數是動態綁定的基礎;動態綁定是實現運行時多態的基礎。
要觸發動態綁定,需滿足兩個條件:
(1) 只有虛函數才能進行動態綁定,非虛函數不進行動態綁定。
(2) 必須通過基類類型的引用或指針進行函數調用。
通過基類指針或基類引用做形參,當實參傳入不同的派生類(或基類)的指針或引用,在函數內部觸發動態綁定,從而來運行時實現多態的。