天天看點

C++的動态綁定和靜态綁定

為了支援 C++ 的多态性,才有了動态綁定和靜态綁定。了解他們的差別有助于更好的了解多态性,以及在程式設計的過程中避免犯錯誤。

需要了解四個名詞:

  • 對象的靜态類型:對象在聲明時所采用的類型,是在編譯期确定的;
  • 對象的動态類型:目前所指對象的類型,是在運作期決定的。對象的動态類型可以更改,但是靜态類型無法更改;

關于對象的靜态類型和動态類型,看如下示例:

class B {};
class C : public B {};
class D : public B {};
D* pD = new D();        //pD的靜态類型是它聲明的類型D*,動态類型也是D*
B* pB = pD;             //pB的靜态類型是它聲明的類型B*,動态類型是pB所指向的對象pD的類型D*
C* pC = new C();
pB = pC;                //pB的動态類型是可以更改的,現在它的動态類型是C*
           
  • 靜态綁定:綁定的是對象的靜态類型,某特性(比如函數)依賴于對象的靜态類型,發生在編譯期,又稱前期綁定 early binding;
  • 動态綁定:綁定的是對象的動态類型,某特性(比如函數)依賴于對象的動态類型,發生在運作期,又稱後期綁定 late binding;
class B {
    void DoSomething();
    virtual void vfun();
};

class C : public B {
    void DoSomething();
    //說明一下,這個子類重新定義了父類的non-virtual函數,
    //這是一個不好的設計,會導緻名稱遮掩;這裡隻是為了說明動态綁定和靜态綁定才這樣使用。
    virtual void vfun();
};

class D : public B {
    void DoSomething();
    virtual void vfun();
};

D* pD = new D();
B* pB = pD;
           

雖然 p D pD pD 和 p B pB pB 都指向同一個對象,

pD->DoSomething()

pB->DoSomething()

調用的不是同一個函數。原因如下:

① 函數

DoSomething

是一個 n o n − v i r t u a l non-virtual non−virtual 函數,它是靜态綁定的,也就是編譯器會在編譯期根據對象的靜态類型來選擇函數;

② p D pD pD 的靜态類型是 D ∗ D* D∗,那麼編譯器在處理

pD->DoSomething()

的時候會将它指向

D::DoSomething()

③ 同理, p B pB pB 的靜态類型是 B ∗ B* B∗,那

pB->DoSomething()

調用的就是

B::DoSomething()

但是

pD->vfun()

pB->vfun()

調用的是同一個函數。原因如下:

vfun()

是一個 v i r t u a l virtual virtual 函數,它動态綁定的,也就是說它綁定的是對象的動态類型, p B pB pB 和 p D pD pD 雖然靜态類型不同,但是他們同時指向一個對象,他們的動态類型是相同的,都是 D ∗ D* D∗,是以,他們的調用的是同一個函數:

D::vfun()

上面都是針對對象指針的情況,對于引用(reference)的情況同樣适用。

指針和引用的動态類型和靜态類型可能會不一緻,但是對象的動态類型和靜态類型是一緻的

D D;
D.DoSomething();
D.vfun();
//以上兩個函數永遠調用的都是D::DoSomething()和D::vfun()
           

至于哪些是動态綁定,哪些是靜态綁定:隻有虛函數才使用的是動态綁定,其他的全部是靜态綁定。

特别需要注意的地方

class B {
 virtual void vfun(int i = 10);
};

class D : public B {
 virtual void vfun(int i = 20);
};

D* pD = new D();
B* pB = pD;
pD->vfun();
pB->vfun();
           

當預設參數和虛函數一起出現的時候情況有點複雜,極易出錯。那就是虛函數是動态綁定的,但是為了執行效率,預設參數是靜态綁定的。

有上面的分析可知

pD->vfun()

pB->vfun()

調用都是函數

D::vfun()

,但是他們的預設參數是多少?

分析一下,預設參數是靜态綁定的,

pD->vfun()

時, p D pD pD 的靜态類型是 D ∗ D* D∗,是以它的預設參數應該是20;同理,

pB->vfun()

的預設參數應該是10。編寫代碼驗證了一下,正确。

該特性的存在是因為為了提高運作期效率。如果預設值是動态綁定的,編譯器就必須有某種方法在運作期為 virtual 函數決定适當的預設值。這比目前實行的 “在編譯期決定” 的機制更慢且更複雜。為了程式的執行速度和編譯器實作上的簡易度, c++選擇了這樣的實作方式,就是為了高執行效率。

對于這個特性,估計沒有人會喜歡。是以,永遠記住:條款37:絕不重新定義繼承而來的預設參數(Never redefine function’s inherited default parameters value.)

參考資料:

  1. 《C++ Primer》
  2. 《Effective C++》
  3. 深入了解C++的動态綁定和靜态綁定
  4. C++之多态性
  5. C++虛函數和虛函數表原理

繼續閱讀