用彙編的眼光看C (之虛函式)

2021-05-28 08:40:17 字數 3886 閱讀 8774

虛函式是物件導向設計中的乙個重要內容。它的出現使得我們只需要相同的介面函式,並可以得到不同的生成結果。但是有些朋友卻知其然,不知其所以然,為什麼會出現這樣的結果,我們可以用一段**說明問題。首先,我們先定義兩個基本型別,乙個是employee,乙個是manager,看過前面一片部落格的朋友應該都有點印象:

view plain

class

employee  

~employee() {}  

virtual

void

print() 

const

};  

class

manager : 

public

employee  

~manager() {}  

void

print() 

const

};  

我們看到,和前面出現的成員函式稍微有一些不同,這裡的print函式之前出現了virtual。然而正是這個virtual發揮了巨大的作用。可以毫不誇張地說,沒有虛函式,基本上就沒有設計模式,也就無法體現c++語言在物件導向設計中的巨大優越性。下面我們看看這個virtual是怎樣發揮作用的?

view plain

76:       employee p;  

0040128d   lea         ecx,[ebp-10h]  

00401290   call        @ilt+45(employee::employee) (00401032)  

00401295   mov         dword ptr [ebp-4],0  

77:       manager m;  

0040129c   lea         ecx,[ebp-14h]  

0040129f   call        @ilt+65(manager::manager) (00401046)  

004012a4   mov         byte ptr [ebp-4],1  

78:       employee* e = &p;  

004012a8   lea         eax,[ebp-10h]  

004012ab   mov         dword ptr [ebp-18h],eax  

79:       e->print();  

004012ae   mov         ecx,dword ptr [ebp-18h]  

004012b1   mov         edx,dword ptr [ecx]  

004012b3   mov         esi,esp  

004012b5   mov         ecx,dword ptr [ebp-18h]  

004012b8   call        dword ptr [edx]  

004012ba   cmp         esi,esp  

004012bc   call        __chkesp (00408870)  

80:       e = &m;  

004012c1   lea         eax,[ebp-14h]  

004012c4   mov         dword ptr [ebp-18h],eax  

81:       e->print();  

004012c7   mov         ecx,dword ptr [ebp-18h]  

004012ca   mov         edx,dword ptr [ecx]  

004012cc   mov         esi,esp  

004012ce   mov         ecx,dword ptr [ebp-18h]  

004012d1   call        dword ptr [edx]  

004012d3   cmp         esi,esp  

004012d5   call        __chkesp (00408870)  

82:   }  

上面是一段函式呼叫的**,**可以稍微有點長。不過沒有關係,我們可以按照**的行數一行一行地去進行說明和理解。

76行:

我們建立了employee型別的乙個變數p,這個可以從後面的employee的建構函式可以看出來

77行:我們建立了manager型別的乙個變數,這個也可以從後面的manager的建構函式看出

78行:

我們建立乙個指標臨時變數e,它儲存了變數p的位址,這一句也比較簡單

79行:

我們發現79句下面共有7句彙編,其中第三句、第六句、第七句是平衡堆疊的時候用的,和我們的呼叫沒有關係。那麼call的edx是什麼東西呢?原來函式呼叫的順序是這樣的:edx -> [ecx]  ->[ebp-0x18],不知道大家看明白了沒有。在記憶體的第乙個位元組記錄乙個指向print函式指標的指標,也就是edx。通過這個edx,我們就可以查詢到位於edx位址的內容是什麼。後來我們提取出來後發現[edx]的內容正是我們要查詢的print函式位址。這裡相當於乙個二次定址的過程。

80行:

我們重新對臨時變數e進行了賦值,此時e儲存的是變數m的位址

81行:我們發現此時的尋找過程和79行驚奇地一致,原因就在於edx的內容不同罷了。也就是指向函式指標的指標發生了變化而已。

試想一下,如果沒有這個virtual函式,以上這段**會發生什麼差別呢?

view plain

76:       employee p;  

0040127d   lea         ecx,[ebp-10h]  

00401280   call        @ilt+45(employee::employee) (00401032)  

00401285   mov         dword ptr [ebp-4],0  

77:       manager m;  

0040128c   lea         ecx,[ebp-14h]  

0040128f   call        @ilt+65(manager::manager) (00401046)  

00401294   mov         byte ptr [ebp-4],1  

78:       employee* e = &p;  

00401298   lea         eax,[ebp-10h]  

0040129b   mov         dword ptr [ebp-18h],eax  

79:       e->print();  

0040129e   mov         ecx,dword ptr [ebp-18h]  

004012a1   call        @ilt+5(employee::print) (0040100a)  

80:       e = &m;  

004012a6   lea         ecx,[ebp-14h]  

004012a9   mov         dword ptr [ebp-18h],ecx  

81:       e->print();  

004012ac   mov         ecx,dword ptr [ebp-18h]  

004012af   call        @ilt+5(employee::print) (0040100a)  

82:   }  

很遺憾,這裡就沒有了動態查詢的過程,所有的列印函式最終都指向了函式employee::print,此時多型性也不復存在了。

用彙編的眼光看C (之虛函式)

虛函式是物件導向設計中的乙個重要內容。它的出現使得我們只需要相同的介面函式,並可以得到不同的生成結果。但是有些朋友卻知其然,不知其所以然,為什麼會出現這樣的結果,我們可以用一段 說明問題。首先,我們先定義兩個基本型別,乙個是employee,乙個是manager,看過前面一片部落格的朋友應該都有點印...

用彙編的眼光看C (之虛函式)

虛函式是物件導向設計中的乙個重要內容。它的出現使得我們只需要相同的介面函式,並可以得到不同的生成結果。但是有些朋友卻知其然,不知其所以然,為什麼會出現這樣的結果,我們可以用一段 說明問題。首先,我們先定義兩個基本型別,乙個是employee,乙個是manager,看過前面一片部落格的朋友應該都有點印...

用彙編的眼光看C (之虛函式)12

虛函式是物件導向設計中的乙個重要內容。它的出現使得我們只需要相同的介面函式,並可以得到不同的生成結果。但是有些朋友卻知其然,不知其所以然,為什 麼會出現這樣的結果,我們可以用一段 說明問題。首先,我們先定義兩個基本型別,乙個是employee,乙個是manager,看過前面一片部落格的朋 友應該都有...