目錄
當我們使用程式呼叫函式的時候,究竟應該執行哪乙個**塊呢?將源**中的函式呼叫解釋為執行特定的函式**塊這個過程被稱為函式名聯編(binding)。
在c語言當中,這非常簡單,因為每個函式名都對應乙個不同的函式。而在c++當中,由於支援了函式過載,使得這個任務變得更加複雜。編譯器必須要檢視函式的引數以及函式名才能確定。好在函式的選擇以及引數在編譯的時候都是確定的,所以這部分聯編在編譯時就能完成,這種聯編被稱為靜態聯編。
在有了虛函式之後, 這個工作變得更加複雜。因為使用哪乙個函式不能在編譯時確定了,因為編譯器不知道使用者將選擇哪個型別的物件。所以,編譯器必須能在程式執行的時候選擇正確的虛函式,這被稱為動態聯編。
在c++當中,動態聯編與指標和引用呼叫方法相關,這是由繼承控制的。前文當中說過,公有繼承建立的is-a關係,使得我們可以用父類指標或引用指向子類的物件。而在c++當中,是不允許將一種型別的位址賦值給另外一種型別的指標的。
下面兩種操作都是非法的:
double x = 2.5;
int *pi = &x; // 非法
long &r = x; // 非法
將派生類引用或指標轉換成基類的引用和指標稱為向上強制轉換(upcasting),這種規則是is-a關係的一部分。因為派生類繼承了基類當中所有的資料成員和成員函式,因此基類成員能夠進行的操作都適用於子類成員,所以向上強制轉換是可傳遞的。
如果反過來呢?將父類物件傳遞給子類指標呢?這種操程式設計客棧作被稱為向下強制轉換(downc程式設計客棧asting),在不使用強制轉換的前提下是不允許的。因為is-a關係通常是不可逆的,派生類當中往往新增了一些資料成員或方法,不能保證在父類物件上一樣還能相容。
我們在使用虛函式的時候其實可以不需要知道當中的實現原理,但是了解了工作原理能夠幫助我們更好地理解理念。另外在c++相關的開發面試當中經常會問起類程式設計客棧似的實現細節。
通常,編譯器處理虛函式的方法是:給每乙個物件新增乙個隱藏成員,這個成員當中儲存了乙個指向函式位址陣列的指標,這種陣列稱為虛函式表。
這個虛函式表中儲存了當前這個類物件的宣告的虛函式的位址,我們來看乙個例子:
class human ;
class hero : public human;
對於human型別的物件,它當中除了類中宣告的內容之外,還會額外多乙個指標,指向groxc乙個列表,比如是[1024,1222]。
這裡的1024和1222分別是show_name和show_all兩個函式**塊的位址。
同樣hero子類當中也會有這樣乙個指標指向乙個虛函式的列表,由於我們在hero子類當中沒有過載show_name方法,所以hero型別的物件中的列表中的第乙個元素仍然是1024。由於我們過載了show_all方法,以及我們新增了乙個show_power的虛函式,因此它的虛函式列表可能是[1024,2333,3777]。
簡單來說,當我們呼叫虛函式的時候, 編譯器會先通過每個物件中的虛函式列表指標拿到虛函式列表。然後在找到對應位置的虛函式**塊的位址,最後進行執行。
顯然這個過程涉及到維護虛函式位址表,以及函式執行時有額外的查表操作,既帶來了儲存空間的消耗,也帶來了效能的消耗。
c 動態聯編與靜態聯編
摘要 本文闡述了靜態聯編和動態聯編的概念和區別,通過具體例項分析了實現動態聯編的條件,指出了虛函式是實現動態聯編的基礎。在c 中,聯編是指乙個電腦程式的不同部分彼此關聯的過程。按照聯編所進行的階段不同,可分為兩種不同的聯編方法 靜態聯編和動態聯編。1.靜態聯編 靜態聯編是指聯編工作在編譯階段完成的,...
C 靜態聯編和動態聯編
聯編就是將模組或者函式合併在一起生成可執行 的處理過程,同時對每個模組或者函式呼叫分配記憶體位址,並且對外部訪問也分配正確的記憶體位址,它是電腦程式彼此關聯的過程。按照聯編所進行的階段不同,可分為兩種不同的聯編方法 靜態聯編和動態聯編。靜態聯編是指在編譯階段就將函式實現和函式呼叫關聯起來,因此靜態聯...
C 動態聯編與靜態聯編
加入自己一些理解.在c 中,聯編是指乙個電腦程式的不同部分彼此關聯的過程。按照聯編所進行的階段不同,可分為兩種不同的聯編方法 靜態聯編和動態聯編。1.靜態聯編 靜態聯編是指聯編工作在編譯階段完成的,這種聯編過程是在程式執行之前完成的,又稱為早期聯編。要實現靜態聯編,在編譯階段就必須確定程式中的操作呼...