從編譯器角度理解虛函數和繼承

在c++中,繼承的概念可以理解爲c中得嵌套結構體,對於各種函數,類中的成員函數,類中的友元函數,各種繼承的虛擬函數,只要從編譯器的角度去理解

就會變得簡單。例如下例:

class D{
 public:
     int func_D();
     int v_func_DD();
};

class D1 : public D{
 public:
     int func_D1();
     virtual int v_func_DD();
};

class D2 : public D1{
 public:
     int func_D2();
     int v_func_DD();
     int d1;
};

//....

  函數的具體實現略

//....

 

int main(int argc, char **argv){

 D2 dd2;
 D *d_ptr;
 D1 *d1_ptr;
 D2 *d2_ptr;


 d_ptr = &dd2;
 d1_ptr = &dd2;
 d2_ptr = &dd2;

 

 d_ptr->v_func_DD();//①
 d1_ptr->v_func_DD();//②

 

d1_ptr->func_D2();//③

d2_ptr->func_D2();//④


 return 0;
}

 

在主函數中,只有一個對象實例,即class D2類型的dd2;D2類直接繼承自D1,間接繼承自D,因爲可以用這兩種類型的指針引用D2類型的對象實例。

從編譯器角度來看時,對於①處,編譯器將做如下處理:

 1.判斷指針類型,發現d_ptr的類型是class D。

 2. 在class D中尋找 v_func_D函數的定義,如果沒有定義,將報錯。

 3. 如果有一個定義,判斷是不是虛函數。

    3.1 如果不是虛函數(正如本例那樣),則在全局符號表中找到相應的修飾後符號,假設爲v_func_DD_classD_void,並根據符號對應的函數地址調用該函數。

    3.2 如果在class D中v_func_DD被定義爲虛函數,則需要動態綁定。
於是,在①處,由於v_func_DD並沒有在class D中被定義爲虛擬函數,則編譯器將判斷該調用爲int D::v_func_DD(),這裏與調用一個全局函數並無區別。

 

對於②處的函數調用,編譯器將會做出相同處理:

 1.判斷指針類型,發現d1_ptr的類型是class D1。

 2. 在class D1中尋找 v_func_D函數的定義,如果沒有定義,將報錯。

 3. 如果有一個定義,判斷是不是虛函數。

    3.1 如果不是虛函數,則在全局符號表中找到相應的修飾後符號,此時爲虛函數,跳過此步。

    3.2 在class D1中v_func_DD被定義爲虛函數,則需要動態綁定,在動態綁定時,由於d1_ptr綁定的對象是D2類型,而此時D2類型的虛函數表已經建立好,並填入了

         int D2::v_func_DD()的地址,所以這裏調用的真正函數是int D2::v_func_DD()。

 

對於③處的處理,編譯器判斷指針類型d1_ptr爲class D1,而D1中並沒有func_D2的定義,即是d1_ptr綁定的是D2對象,編譯器也是不允許這種情況的,所以編譯器將報

未定義的錯誤。

 

對於④的處理,由於指針是class D2類型,因此會正確調用int D2::func_D2()函數,這是,調用該成員函數和調用一個全局函數的情況並無區別,只不過編譯器根據類型名,

參數名,和函數名對最後生成的全局符號進行了mangling。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章