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

來源:互聯網
上載者:User

【 聲明:著作權,歡迎轉載,請勿用於商業用途。  聯絡信箱:feixiaoxing @163.com】  

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

class employee{public:employee() { }~employee() {}virtual void print() const { printf("employee!\n");}};class manager : public employee{public:manager() {}~manager() {}void print() const {printf("manager!\n");}};

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

76:       employee p;0040128D   lea         ecx,[ebp-10h]00401290   call        @ILT+45(employee::employee) (00401032)00401295   mov         dword ptr [ebp-4],077:       manager m;0040129C   lea         ecx,[ebp-14h]0040129F   call        @ILT+65(manager::manager) (00401046)004012A4   mov         byte ptr [ebp-4],178:       employee* e = &p;004012A8   lea         eax,[ebp-10h]004012AB   mov         dword ptr [ebp-18h],eax79:       e->print();004012AE   mov         ecx,dword ptr [ebp-18h]004012B1   mov         edx,dword ptr [ecx]004012B3   mov         esi,esp004012B5   mov         ecx,dword ptr [ebp-18h]004012B8   call        dword ptr [edx]004012BA   cmp         esi,esp004012BC   call        __chkesp (00408870)80:       e = &m;004012C1   lea         eax,[ebp-14h]004012C4   mov         dword ptr [ebp-18h],eax81:       e->print();004012C7   mov         ecx,dword ptr [ebp-18h]004012CA   mov         edx,dword ptr [ecx]004012CC   mov         esi,esp004012CE   mov         ecx,dword ptr [ebp-18h]004012D1   call        dword ptr [edx]004012D3   cmp         esi,esp004012D5   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函數,以上這段代碼會發生什麼差別呢?

76:       employee p;0040127D   lea         ecx,[ebp-10h]00401280   call        @ILT+45(employee::employee) (00401032)00401285   mov         dword ptr [ebp-4],077:       manager m;0040128C   lea         ecx,[ebp-14h]0040128F   call        @ILT+65(manager::manager) (00401046)00401294   mov         byte ptr [ebp-4],178:       employee* e = &p;00401298   lea         eax,[ebp-10h]0040129B   mov         dword ptr [ebp-18h],eax79:       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],ecx81:       e->print();004012AC   mov         ecx,dword ptr [ebp-18h]004012AF   call        @ILT+5(employee::print) (0040100a)82:   }

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

【預告: 下一片部落格將介紹類中的靜態變數和靜態函數】
  

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.