C++ Early Binding

開發平台: Visual Studio 2010

程式碼一

class InchArea

{

int getlength(){

return inch;

}

int getArea(){

return int(pow(getlength(), 2));

}

}

class FeetArea:public InchArea

{

int getlength(){

return inch;

}

}

void main (void)

{

InchArea ia(5);

ia.getArea();

FeetArea fa(8);

fa.getArea();

}

__________________________________________________________________________

程式碼二

class Employee

{

void f2(){}

void test(Emp& x)

{

x.f2();

}

}

class Sales :public Employee

{

void f2(){}

void test(Emp& x)

{

x.f2();

}

}

void main()

{

Sales s;

Test(s);

}

程式碼一

ia.getArea();>>>此行會呼叫 int getArea()此函式然後再到InchArea

類別執行int getlength()此函式

fa.getArea();>>>此行也是與上述同樣道理

想知道編譯器在編譯期是怎樣的判斷方式會造成以上描述現象???

______________________________________________________________

程式碼二

在Main()函式裡,建立sales類別的物件s

呼叫Test(s)函式,但卻是執行Employee類別void test(Emp& x)

而不是Sales類別void test(Emp& x)

想知道編譯器在編譯期是怎樣的判斷方式會造成以上描述現象???

Update:

func(*pia); // 呼叫 FeetArea::getlength();

func(*pfa); // 呼叫 FeetArea::getlength();

以上兩個應該不對吧

因*pia=&ia(因ia再函式裡宣告為InchArea)

*pfa=&ia

所以二者是呼叫InchArea::getlength();吧??

2 Answers

Rating
  • 其威
    Lv 7
    8 years ago
    Favorite Answer

    在 C++ 中,所有的東西都是 early binding。你不應該跟把 late binding 與 dynamic dispatch 搞混...

    因為 C++ 是 strong typing 的程式語言,在執行之前該呼叫什麼函式就會全部知道,不會有到了執行期還搞不清楚到底要呼叫什麼函式的狀況(或是說,一個型別的變數突然變成另一個型別的狀況)...

    即使是 virtual function,他也只是在某幾個 function 中選一個實際要用的 function 出來,而不是到了臨呼叫前才去想辦法 resolve 需要呼叫的函式。

    回到你的問題,編譯器怎樣決定要呼叫的是哪個函式...

    === 分隔線 ===

    若某個 member function 不是 virtual,編譯器會根據型別來決定實際要呼叫的函式。

    以你的 1 為例,當你 invoke getlength() 的時候的型別是 InchArea,他就會呼叫 InchArea::getlength(),若 invoke getlength() 時的型別是 FeetArea,他就會呼叫 FeetArea::getlength()。

    FeetArea fa(123);

    fa.getlength(); // 呼叫 FeetArea::getlength();

    InchArea *pia = &fa;

    pia->getlength(); // 呼叫 InchArea::getlength();

    FeetArea *pfa = &fa;

    pfa->getlength(); // 呼叫 FeetArea::getlength();

    void func(InchArea &ia)

    {

    ia.getlength();

    }

    func(fa); // 呼叫 InchArea::getlength();

    func(*pia); // 呼叫 InchArea::getlength();

    func(*pfa); // 呼叫 InchArea::getlength();

    在這種時候,要呼叫哪個 function,是在編譯期就決定好的。

    他看到變數「表面」的型別是什麼,就會去找該型別的對應函式,而不會去檢查變數「實際」的型別。

    這同時也回答你第二個 Sales 的問題,因為他看到的就是 Employee,所以就去找了 Employee::test(),不會管實際拿到的是 Employee 還是 Sales。

    === 分隔線 ===

    可是如果你在 getlength() 前面加上 virtual,他就會依照實際的型別來呼叫函式。

    class InchArea {

    virtual int getlength() { ... }

    };

    class FeetArea: public InchArea {

    virtual int getlength() { ... }

    };

    那麼以上所有的呼叫都會取決於物件的實際型別:

    fa.getlength(); // 呼叫 FeetArea::getlength();

    pia->getlength(); // 呼叫 FeetArea::getlength();

    pfa->getlength(); // 呼叫 FeetArea::getlength();

    func(fa); // 呼叫 FeetArea::getlength();

    func(*pia); // 呼叫 FeetArea::getlength();

    func(*pfa); // 呼叫 FeetArea::getlength();

    在 Sales 那邊也是一樣,若你在 f2() 之前加上 virtual,他就會知道呼叫該函式的時候要根據物件的實際型別來選擇對應函式,而不是表面上看到的型別。

    === 分隔線 ===

    這是一個效能考量。

    呼叫 virtual function 是有「成本」的,會比呼叫 non-virtual function

    慢。

    virtual function 為保證「呼叫到正確的 function」的能力,會在物件中增加一個 virtual function table(先增加了記憶體成本),然後在執行期根據物件的實際型態來決定要呼叫的函式(本來直接呼叫變成間接呼叫)。

    所以當你沒有特別告訴編譯器說「我這個函式在子類別中也會有,你即使拿到一個母類別的參考或指標也必須呼叫到正確的子類別中的函式喔!」的話,他會以速度最快的方法來作。

    2011-09-14 16:24:59 補充:

    對了,我預設你說的 C++ 是 Native C++ 而非 Managed C++。

    因為 .Net VM 提供 reflection API 在 runtime 呼叫你不知道的型別的函式。

    2011-09-15 13:55:21 補充:

    FeetArea fa;

    InchArea *pia = &fa;

    所以 pia 實際上指到的是一個 FeetArea 物件(而不僅是 InchArea 物件),在透過 pointer 或 reference 呼叫來 invoke virtual member function 的時候,他會根據實際的型別去找對應的函式。

    所以,若 getlength() 是 virtual,他會呼叫 FeetArea::getlength()。

    2011-09-15 13:59:53 補充:

    所謂「實際的型別」就是在配置物件時使用的型別。

    意見 2 中的兩行 code,pia 這個指標宣告用的型別是 InchArea,但是他實際上指到 個 FeetArea。

    所以物件的「實際型別」是 FeetArea 而非 InchArea。

    func() 這函式亦如此,宣告的雖然是 InchArea &,但是實際傳給他的若是個 FeetArea,那他實際的型別就是 FeetArea。

    2011-09-15 14:03:14 補充:

    所謂「配置物件時使用的型別」你可以想像成「呼叫的是哪個 c-tor」。

    FeetArea fa; // 呼叫 FeetArea::FeetArea()

    InchArea ia; // 呼叫 InchArea::InchArea()

    InchArea *pia = &fa; // 沒有呼叫 c-tor

    *pia 實際上是個 FeetArea 物件,所以你透過 pia 來 invoke virtual function,他會呼叫 FeetArea 的 member function 而非 InchArea 的。

    2011-09-15 14:08:54 補充:

    如果你在宣告 func 的時候沒有用參考來傳,例如宣告成這樣:

    void func(InchArea ia);

    那麼就會遇到恐怖的 partial copy...

    也就是說當傳 FeetArea 的時候他只會複製前面 InchArea 的部份,FeetArea 新增的部份不會被複製到。

    而由於他呼叫的是 InchArea 的 copy c-tor,所以當你使用 ia 來 invoke member function 的時候他會呼叫 InchArea 中定義的函式。

    2011-09-15 20:25:57 補充:

    因為在編譯 getArea() 的時候,他只知道 InchArea 不知道 FeetArea 啊...

    由於你的 getlength() 不是 virtual function,所以他會認為針對 getlength() 的呼叫在編譯期就可以決定。

    而對於 getArea() 來說,他只看得到前面的 InchArea 的部份,看不到 FeetArea 這 super class(有點像 partial copy 那樣的狀況)。

    所以他會用 InchArea::getlength()。

  • 8 years ago

    func(*pia); // 呼叫 FeetArea::getlength();

    func(*pfa); // 呼叫 FeetArea::getlength();

    以上兩個應該不對吧

    因*pia=&ia(因ia再函式裡宣告為InchArea)

    *pfa=&ia

    所以二者是呼叫InchArea::getlength();吧??

    還有物件的實際型別指的是甚麼意思??

    書本上有提到,但並非很清楚

    2011-09-15 16:12:35 補充:

    以上解說了解

    那在程式碼一

    void main (void)

    {

    FeetArea fa(8);

    fa.getArea();

    }

    fa的型態是FeetArea卻不是執行FeetArea::getlength()

    卻是執行InchArea::getlength(),覺得疑惑????

    2011-09-15 20:52:17 補充:

    感謝...帕拉提斯前輩

Still have questions? Get your answers by asking now.