1. 程式人生 > >C++之靜態聯編與動態聯編及virtual關鍵字的作用

C++之靜態聯編與動態聯編及virtual關鍵字的作用

定義


將一個呼叫函式連線上正確的被呼叫函式,這個過程就叫做函式的聯編,簡稱聯編。在C++中,一共有兩種聯編的方式:

靜態聯編

#define:靜態聯編是指聯編工作出現在編譯連線階段
特點:① 靜態聯編就是指被呼叫函式和執行呼叫函式之間的關係以及它們在記憶體中的地址在編譯的時候已經確定好了,執行時不會發生變化。
② 由於物件不用對自身進行跟蹤,因此速度浪費比較小,但是靈活性較差。

動態聯編

#define:動態聯編是指在程式執行的時候才進行的聯編工作。
特點:① 由於編譯程式在編譯階段並不能確切知道將要呼叫的函式,只有在程式執行時才能確定將要呼叫的函式。要確切之道該呼叫的函式,就必須要求聯編工作在程式執行時才能進行。
② 雖然可以追蹤物件,靈活性較強,但是速度浪費嚴重。

virtual關鍵字的作用

首先,我們來看幾個例子:

//例1 用父類物件的指標指向子類物件
#include <iostream>
using namespace std;
 
class father
{
public:
    father(){};
    father(int i){age=i;}
    void print()const{cout<<"father's age is "<<age<<endl;}
protected: //protected宣告的成員可以被派生類訪問
    int age;
};
 
class son:public father
{
public:
    son(){};
    son(int j){age=j;}
    void print()const{cout<<"son's age is "<<age<<endl;}
};
 
int main()
{
    cout<<"一般情況:\n";
    father dad(56);
    dad.print();
    son boy(22);
    boy.print();
    cout<<"指標情況:\n";
    father *liu=new father(56);
    liu->print();
    son *uniqueliu=new son(22);
    //father *uniqueliu=new son(22);
    uniqueliu->print();
}
這個程式的執行結果:

上面這個結果很好理解吧,我就不解釋了。但是如果我們這裡把程式33行的註釋去掉,把32行註釋起來。那麼程式執行的結果就會出現如下的情況:


這個就很奇怪了。我們的初衷是想用一個父類指標來指向一個子類物件。但是最後呼叫的print函式卻是父類物件的。這個原因就是因為我們沒有在父類中的print()前面加上關鍵字virtual的原因了。這是因為在函式print()函式前面加上了關鍵字virtual,就表示該函式是有多種形態的,說白了,就是這個print()函式可以被很多物件所擁有,而且各自實現的功能是不一樣的,這樣一來,就是先了多型。總結一下,我們只要在基類的成員函式前面加上virual,那麼就算派生類的物件重新實現了同名函式,編譯器就會自動判斷是哪個物件呼叫了它,然後用該物件的同名函式,而不會採用基類的函數了。程式如下所示:

//例2 用父類物件的指標指向子類物件+virtual
#include <iostream>
using namespace std;
 
class father
{
public:
    father(){};
    father(int i){age=i;}
    virtual void print()const{cout<<"father's age is "<<age<<endl;}
protected: //protected宣告的成員可以被派生類訪問
    int age;
};
 
class son:public father
{
public:
    son(){};
    son(int j){age=j;}
    void print()const{cout<<"son's age is "<<age<<endl;}
};
 
int main()
{
    cout<<"一般情況:\n";
    father dad(56);
    dad.print();
    son boy(22);
    boy.print();
    cout<<"指標情況:\n";
    father *liu=new father(56);
    liu->print();
    //son *uniqueliu=new son(22);
    father *uniqueliu=new son(22);
    uniqueliu->print();
}
這樣,我們在程式的第10行上加上了virtual關鍵字。它就將基類的print()函式宣告成了虛擬函式。這樣一來,我們就可以呼叫子類的print()函數了,下圖說明了真相:



virtual與聯編之間的關係


相對於例1來講,在這裡面呼叫的關係就是靜態聯編,因為在程式的編譯階段就已經把物件和它們所指向的函式緊緊地聯絡在一起了,執行的時候自然就不會有任何改變。而對於例2來講,由於在父類的print()函式前面加上了關鍵字virtual,那麼這個時候父類中的print()函式就變成了虛擬函式,虛擬函式就可以實現執行時的動態聯編。這是因為virtual會讓編譯器自動地區尋找與之對應的物件,所以輸出才會如紅框所示,“son's age is 22”。

特別注意一點!!!只有在使用指標或者是引用的時候,才能夠實現動態聯編。