解析動態聯編 上

2021-04-12 17:02:20 字數 2832 閱讀 8094

文章摘要

多型性是c++最主要的特徵,多型性的實現得益於c++中的動態聯編技術。文章通過對動態聯編的關鍵技術虛函式表進行深入的剖析,解析的動態聯編的過程極其技術要領。

關鍵字

多型性 動態聯編 vtable 虛函式

文章正文

一 從多型性談動態聯編的必要性

在 進入主題之前先介紹一下聯編的概念。聯編就是將模組或者函式合併在一起生成可 執行**的處理過程,同時對每個模組或者函式呼叫分配記憶體位址,並且對外部訪問也分配正確的記憶體位址。按照聯編所進行的階段不同,可分為兩種不同的聯編方 法:靜態聯編和動態聯編。在編譯階段就將函式實現和函式呼叫關聯起來稱之為靜態聯編,靜態聯編在編譯階段就必須了解所有的函式或模組執行所需要檢測的信 息,它對函式的選擇是基於指向物件的指標(或者引用)的型別。反之在程式執行的時候才進行這種關聯稱之為動態聯編,動態聯編對成員函式的選擇不是基於指標 或者引用,而是基於物件型別,不同的物件型別將做出不同的編譯結果。c語言中,所有的聯編都是靜態聯編。c++中一般情況下聯編也是靜態聯編,但是一旦涉 及到多型性和虛函式就必須使用動態聯編。

多型性是物件導向的核心,它的最主要的思想就是可以採用多種形式的能力,通過乙個使用者名字或者使用者介面完成不同的實現。通常多型性被簡單的描述為"乙個介面,多個實現。在c++裡面具體的表現為通過基類指標訪問派生類的函式和方法。

下面我們看乙個靜態聯編的例子,這種靜態聯編導致了我們不希望的結果。

//1.cpp

1. #i nclude

2. class shape{

3. public:

4. void draw(){cout<<"i am shape"《程式的輸出結果我們希望是"i am circle",但事實上卻輸出了"i am shape"的結果,造成這個結果的原因是靜態聯編。靜態聯編需要在編譯時候就確定函式的實現,但事實上編譯器在僅僅知道shape的位址時候無法獲取正 確的呼叫函式,它所知道的僅是shape::draw(),最終結果只能是draw操作束縛到shape類上。產生"i am shape"的結果就不足為奇了。

為了能夠引起動態聯編,我們只需要將需要動態聯編的函式宣告為虛函式即可。動態聯編只對虛函式起作用。我們在通過基類而且只有通過基類訪問派生類的時候, 只要這個基類中直接的或者間接(從上上層繼承)的包含虛函式,動態聯編將自動喚醒。下面我們將上面的程式稍微改一下。

//2.cpp

1. #i nclude

2. class shape{

3. public:

4.virtualvoid draw(){cout<<"i am shape"《程式執行得到了正確的結果"i am circle"。**在vc6.0中執行。

到目前為止我們不清楚動態聯編的執行機制,但我 們可以做個猜測。正如上面所說,對於函式的實際的物件型別不同,聯編結果也應該不同。在靜態聯編中,執行的困難在於無法通過基類知道需要聯編的子物件的確 切型別。在1.cpp中shape的派生類既可能是circle,也可能是其餘的rectangle或者square等等,到底應該靜態聯編哪乙個呢。迷 惑正在於此。動態聯編在編譯的時候應該也是不知道聯編的確切物件型別的,(如果知道的話就成了靜態聯編了),因此它只能通過一定的機制,使得在執行時候能 夠找到和呼叫正確的函式體。可以想象,為了達到這個目的,一些相關資訊應該封裝在物件自身中。這些資訊有點象身份證明,標識自己,這樣在動態聯編的時候, 編譯器可以根據這些標記找到相應的函式體,"不要跑,就是你了"。

實際上的動態聯編過程是什麼樣的呢。

二 物件型別資訊

為了證明我們的猜想,我們用下面的乙個程式進行測試,下面的程式將獲取普通的類和包含虛函式的類的位元組大小。程式**如下。

//3.cpp

1. #i nclude

2. class shape_novirtual{

3. int a;

4. public:

5. void draw(){cout<<"shape_novirtual::draw()"sizeof(int)4

sizeof(class shape_novirtual):4

sizeof(void*):4

sizeof(class shape_virtual1):8

sizeof(class shape_virtual2):8

press any key to continue

從上面可以看出,沒有虛函式的類shape_novirtual的大小為4,正好為int a的大小。而帶有虛函式的類shape_virtual1和shape_virtual2的大小除了int a的大小還多出了4格個位元組的大小,這個大小正好是void*指標的大小。到現在為止我們基本上可以說帶有虛函式的物件自身確實插入了一些指標資訊,而且 這個指標資訊並不隨著虛函式的增加而增大。

如果我們將每個類的成員變數int a去掉,vc6.0執行結果就會變成下面的情況。

sizeof(int)4

sizeof(class shape_novirtual):1

sizeof(void*):4

sizeof(class shape_virtual1):4

sizeof(class shape_virtual2):4

press any key to continue

上面的執行結果應該讓人感到例外。既然size(int)為4,現在沒有了這個成員變數,類shape_novirtual應該位元組大小為0,但事 實上c++編譯器不允許物件為零長度。試想乙個長度為0的物件在記憶體中怎麼存放?怎麼獲取它的位址?為了避免這種情況,c++強制給這種類插入乙個預設成 員,長度為1。如果有自定義的變數,變數將取代這個預設成員。 

解析動態聯編 轉貼

日期 2005 4 15 字型 大 中 小 文章摘要 多型性是c 最主要的特徵,多型性的實現得益於c 中的動態聯編技術。文章通過對動態聯編的關鍵技術虛函式表進行深入的剖析,解析的動態聯編的過程極其技術要領。關鍵字 多型性 動態聯編 vtable 虛函式 文章正文 一 從多型性談動態聯編的必要性 在進...

解析動態聯編 下篇

三 虛函式表vtable 動態聯編過程跟我們猜測的大致相同。編譯器在執行過程中遇到virtual關鍵字的時候,將自動安裝動態聯編需要的機制,首先為這些包含virtual函式的類 注意不是類的例項 即使是祖先類包含虛函式而本身沒有 建立一張虛函式表vtable。在這些虛函式表中,編譯器將依次按照函式宣...

銀聯報文解析

寫了乙個公用的beanutils物件複製工具類,傳入兩個字串得到兩個模型,乙個成功複製,另乙個複製後值全為null 兩個字串分別為 string a string q 公共方法為 public static t convobject string cominfo,classcls throws il...