C++多態:虛函數、指針、虛函數表

本文總結了http://blog.csdn.net/hackbuteer1/article/details/7475622http://blog.csdn.net/zoopang/article/details/14071779http://blog.csdn.net/haoel/article/details/1948051/幾位老師的博客

首先聲明:博客是對自己學習知識的總結,避免遺忘方便以後學習。爲了避免誤導讀者,如果哪位發現有描述不對的地方,希望告訴我,謝謝。

一、什麼是多態
關於多態,簡而言之就是用父類型別的指針指向其子類的實例,然後通過父類的指針調用實際子類的成員函數。這種技術可以讓父類的指針有“多種形態”,這是一種泛型技術。所謂泛型技術,說白了就是試圖使用不變的代碼來實現可變的算法。比如:模板技術,RTTI技術,虛函數技術,要麼是試圖做到在編譯時決議,要麼試圖做到運行時決議
C++多態性是通過虛函數來實現的,虛函數允許子類重新定義成員函數,而子類重新定義父類的做法稱爲覆蓋(override),或者稱爲重寫。

備註:重寫、隱藏、重載的概念
重寫:子類重新定義父類的做法稱爲覆蓋(override),或者稱爲重寫,只有重寫了虛函數的才能算作是體現了C++多態性。
隱藏:如果重寫的是父類的成員函數,則稱爲隱藏
重載:重載指得是同一個類內部,有多個同名函數,同名函數裏面的參數類型或者參數個數不同(和返回值沒有關係),???這裏有個疑問,不知道程序是在編譯的時候還是在運行的時候知道要調用的函數的真正地址,我感覺是編譯的時候。
早晚綁定的問題:多態與非多態的實質區別就是函數地址是早綁定還是晚綁定。如果函數的調用,在編譯器編譯期間就可以確定函數的調用地址,並生產代碼,是靜態的,就是說地址是早綁定的。而如果函數調用的地址不能在編譯器期間確定,需要在運行時才確定,這就屬於晚綁定。

二 如何實現多態
最常見的用法就是聲明基類的指針,利用該指針指向任意一個子類對象,調用相應的虛函數,可以根據指向的子類的不同而實現不同的方法。如果沒有使用虛函數的話,即沒有利用C++多態性,則利用基類指針調用相應的函數的時候,將總被限制在基類函數本身,而無法調用到子類中被重寫過的函數。因爲沒有多態性,函數調用的地址將是一定的,而固定的地址將始終調用到同一個函數,這就無法實現一個接口,多種方法的目的了。

#include<iostream>  
using namespace std;  

class A  
{  
public:  
    void foo()  
    {  
        printf("1\n");  
    }  
    virtual void fun()  
    {  
        printf("2\n");  
    }  
};  
class B : public A  
{  
public:  
    void foo()  
    {  
        printf("3\n");  
    }  
    void fun()  
    {  
        printf("4\n");  
    }  
};  
int main(void)  
{  
    A a;  
    B b;  
    A *p = &a;  
    p->foo();  
    p->fun();  
    p = &b;  
    p->foo();  
    p->fun();
    //直接賦值無法實現多態
    a = b;
    a.foo();  //仍然輸出的是A的函數
    a.fun();
    return 0;  
} 

第一個p->foo()和p->fuu()都很好理解,本身是基類指針,指向的又是基類對象,調用的都是基類本身的函數,因此輸出結果就是1、2。
  第二個輸出結果就是1、4。p->foo()和p->fuu()則是基類指針指向子類對象,正式體現多態的用法,p->foo()由於指針是個基類指針,指向是一個固定偏移量的函數,因此此時指向的就只能是基類的foo()函數的代碼了,因此輸出的結果還是1。而p->fun()指針是基類指針,指向的fun是一個虛函數,由於每個虛函數都有一個虛函數列表,此時p調用fun()並不是直接調用函數,而是通過虛函數列表找到相應的函數的地址,因此根據指向的對象不同,函數地址也將不同,這裏將找到對應的子類的fun()函數的地址,因此輸出的結果也會是子類的結果4。

三、虛函數表
後面會繼續補充

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