C++淺析——虛函數的動態和靜態綁定

源自一道面試題,覺得很有意思

class CBase
{
public:
        virtual        void PrintData(int nData = 111);
};
void        CBase::PrintData(int nData /* = 111 */)
{
        printf("CBase::PrintData, nData = %d\n", nData);
}

class CDerived : public CBase
{
public:
        void PrintData(int nData = 222);
};
void        CDerived::PrintData(int nData /* = 111 */)
{
        printf("CDerived::PrintData, nData = %d\n", nData);
}
在main()中做如下調用:

 CDerived        oCDerived;
 CBase*        pCBase = (CBase*)&oCDerived;
        
 pCBase->PrintData();
(*pCBase).PrintData();
 oCDerived.PrintData();
大家先猜猜輸出結果是什麼?


是不是更奇怪,我們看看反彙編的代碼:

14、oCDerived.PrintData();
push        0DEh
lea         ecx,[ebp-4]
call        @ILT+25(CDerived::PrintData) (0040101e)        ;直接調用CDerived::PrintData(),無虛表取址過程

15、((CBase)oCDerived).PrintData();
mov         esi,esp
push        6Fh                                                ;壓入CBase::PrintData()形參
lea         ecx,[ebp-4]
push        ecx                                                ;壓入oCDerived的this指針
lea         ecx,[ebp-10h]
call        @ILT+10(CBase::CBase) (0040100f)                ;調用CBase拷貝構造函數新創建了一個CBase對象
mov         dword ptr [ebp-14h],eax
mov         edx,dword ptr [ebp-14h]                        
mov         eax,dword ptr [edx]                                ;取新CBase對象的虛表
mov         ecx,dword ptr [ebp-14h]
call        dword ptr [eax]                                ;調用新CBase對象的虛表的第一個函數

15.1、CBase::CBase拷貝構造函數;
mov         dword ptr [ebp-4],ecx                        ;取this指針
mov         eax,dword ptr [ebp-4]                        
mov         dword ptr [eax],offset CBase::`vftable' (00425024)        ;虛表地址賦值,直接用的CBase虛表,而沒有用CDerived的虛表
mov         eax,dword ptr [ebp-4]                        ;將this指針給eax返回

16、pCDerived->PrintData();
mov         esi,esp
push        0DEh
mov         ecx,dword ptr [ebp-8]                        ;取pCDerived
mov         edx,dword ptr [ecx]                                ;取虛表
mov         ecx,dword ptr [ebp-8]                        ;放入this指針
call        dword ptr [edx]                                ;調用虛表的第一個函數,即PrintData()

從上述反彙編代碼中可以看出:

1、oCDerived.PrintData()對象調用是靜態綁定的

2、pCDerived->PrintData()指針調用是動態綁定的,而且(*pCDerived).PrintData(),即指針實例化後的對象調用也是動態綁定的(可自行看反彙編代碼)

3、對象的類型轉換中會參數新的對象,同時會調用新對象的構造拷貝函數,但由於CBase的默認拷貝構造函數爲CBase::CBase(CBase&)形式,故仍會取CBase的虛表地址給新對象的虛表初始化,故((CBase)oCDerived).PrintData();實際上是調用了一個全新的CBase對象的PrintData()函數,因此不建議對對象進行類型轉換,因爲實際調用過程中已經不是原來的那個對象了,如果對象函數中涉及對成員的賦值或改動操作,那實際上是不會生效的。

發佈了27 篇原創文章 · 獲贊 2 · 訪問量 3萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章