C++的靜態繫結和動態綁定概述

來源:互聯網
上載者:User

C++在物件導向編程中,存在著靜態繫結和動態綁定的定義,本節即是主要講述這兩點區分。

我是在一個類的繼承體系中分析的,因此下面所說的對象一般就是指一個類的執行個體。

首先我們需要明確幾個名詞定義:

靜態類型:對象在聲明時採用的類型,在編譯期既已確定;

動態類型:通常是指一個指標或引用目前所指對象的類型,是在運行期決定的;

靜態繫結:綁定的是靜態類型,所對應的函數或屬性依賴於對象的靜態類型,發生在編譯期;

動態綁定:綁定的是動態類型,所對應的函數或屬性依賴於對象的動態類型,發生在運行期;

從上面的定義也可以看出,非虛函數一般都是靜態繫結,而虛函數都是動態綁定(如此才可實現多態性)。

先看代碼和運行結果:

class A{public:    /*virtual*/ void func(){ std::cout << "A::func()\n"; }};class B : public A{public:    void func(){ std::cout << "B::func()\n"; }};class C : public A{public:    void func(){ std::cout << "C::func()\n"; }};

下面逐步分析測試代碼及結果,

1 C* pc = new C(); //pc的靜態類型是它聲明的類型C*,動態類型也是C*;

2 B* pb = new B(); //pb的靜態類型和動態類型也都是B*;

更多精彩內容:http://www.bianceng.cnhttp://www.bianceng.cn/Programming/cplus/

3 A* pa = pc;      //pa的靜態類型是它聲明的類型A*,動態類型是pa所指向的對象pc的類型C*;

4 pa = pb;         //pa的動態類型可以更改,現在它的動態類型是B*,但其靜態類型仍是聲明時候的A*;

5 C *pnull = NULL; //pnull的靜態類型是它聲明的類型C*,沒有動態類型,因為它指向了NULL;

如果明白上面代碼的意思,請繼續,

1 pa->func();      //A::func() pa的靜態類型永遠都是A*,不管其指向的是哪個子類,都是直接調用A::func();

2 pc->func();      //C::func() pc的動、靜態類型都是C*,因此調用C::func();

3 pnull->func();   //C::func() 不用奇怪為什麼null 指標也可以調用函數,因為這在編譯期就確定了,和指標空不空沒關係;

如果注釋掉類C中的func函數定義,其他不變,即

class C : public A{};    pa->func();      //A::func() 理由同上;pc->func();      //A::func() pc在類C中找不到func的定義,因此到其基類中尋找;pnull->func();   //A::func() 原因也解釋過了;

如果為A中的void func()函數添加virtual特性,其他不變,即

 

class A{public:    virtual void func(){ std::cout << "A::func()\n"; }};    pa->func();      //B::func() 因為有了virtual虛函數特性,pa的動態類型指向B*,因此先在B中尋找,找到後直接調用;pc->func();      //C::func() pc的動、靜態類型都是C*,因此也是先在C中尋找;pnull->func();   //null 指標異常,因為是func是virtual函數,因此對func的調用只能等到運行期才能確定,然後才發現pnull是null 指標;

分析:

在上面的例子中,

1. 如果基類A中的func不是virtual函數,那麼不論pa、pb、pc指向哪個子類對象,對func的調用都是在定義pa、pb、pc時的靜態類型決定,早已在編譯期確定了。

同樣的null 指標也能夠直接調用no-virtual函數而不報錯(這也說明一定要做null 指標檢查啊!),因此靜態繫結不能實現多態;

2. 如果func是虛函數,那所有的調用都要等到運行時根據其指向對象的類型才能確定,比起靜態繫結自然是要有效能損失的,但是卻能實現多態特性;

本文代碼裡都是針對指標的情況來分析的,但是對於引用的情況同樣適用。

至此總結一下靜態繫結和動態綁定的區別:

1. 靜態繫結發生在編譯期,動態綁定發生在運行期;

2. 對象的動態類型可以更改,但是靜態類型無法更改;

3. 要想實現動態,必須使用動態綁定;

4. 在繼承體系中只有虛函數使用的是動態綁定,其他的全部是靜態繫結;

建議:

絕對不要重新定義繼承而來的非虛(non-virtual)函數(《Effective C++ 第三版》條款36),因為這樣導致函數調用由對象聲明時的靜態類型確定了,而和對象本身脫離了關係,沒有多態,也這將給程式留下不可預知的隱患和莫名其妙的BUG;

另外,在動態綁定也即在virtual函數中,要注意預設參數的使用。當預設參數和virtual函數一起使用的時候一定要謹慎,不然出了問題怕是很難排查。

看下面的代碼:

class E{public:    virtual void func(int i = 0)    {         std::cout << "E::func()\t"<< i <<"\n";    }};class F : public E{public:    virtual void func(int i = 1)    {        std::cout << "F::func()\t" << i <<"\n";    }};    void test2(){    F* pf = new F();    E* pe = pf;    pf->func(); //F::func() 1  正常,就該如此;    pe->func(); //F::func() 0  哇哦,這是什麼情況,調用了子類的函數,卻使用了基類中參數的預設值!}

為什麼會有這種情況,請看《Effective C++ 第三版》 條款37。

這裡只給出建議:

絕對不要重新定義一個繼承而來的virtual函數的預設參數值,因為預設參數值都是靜態繫結(為了執行效率),而virtual函數卻是動態綁定。

作者:lizhenghn (lizhenghn@gmail.com)

出處:http://www.cnblogs.com/lizhenghn

相關文章

E-Commerce Solutions

Leverage the same tools powering the Alibaba Ecosystem

Learn more >

Apsara Conference 2019

The Rise of Data Intelligence, September 25th - 27th, Hangzhou, China

Learn more >

Alibaba Cloud Free Trial

Learn and experience the power of Alibaba Cloud with a free trial worth $300-1200 USD

Learn more >

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。