1. 程式人生 > >詳解C++中的純虛擬函式(虛擬函式區別)&多型性 以及理解

詳解C++中的純虛擬函式(虛擬函式區別)&多型性 以及理解

#include <iostream>
#include <cstdio>


using namespace std;


class A
{
public:
    void foo()
    {
        printf("1\n");
    }
    virtual void fuu()
    {
        printf("2\n");    
    }
};


class B:public A
{
public :
    void foo()
    {
        printf("3\n");
    }
    void fuu()
    {
        printf("4\n");
    }
};


int main()
{
    A a;
    B b;
    
    A *p = &a;
    cout<< "p->foo()---" ; p->foo() ; 
    cout<<"p->fuu()---";p->fuu();    


    cout <<"************************"<<endl;
    p=&b;
    cout<<"p->foo()---";p->foo();    
    cout<<"p->fuu()---";p->fuu();    
    
    cout <<"************************-"<<endl;
    
    B *ptr =(B *)&a;
    cout<<"ptr->foo()----";ptr->foo();
    cout<<"ptr->fuu()-----";ptr->fuu();
    return 0;

}

輸出結果為:

p->foo()---1
p->fuu()---2   


************************

p->foo()---1   
p->fuu()---4 
    
************************

   
ptr->foo()----3
ptr->fuu()-----2

從中得出結論 如果是非虛擬函式,則實際是什麼類(就是最總經過強制轉換之後的類),則執行該類,如果是虛擬函式,則是根據最原始定義的類,執行該類 ;這裡主要原因是:

非虛擬函式,指向是一個固定偏移量的函式,而虛擬函式則需要考慮虛擬函式列表問題。

如下是網上給出的解釋:

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

  第三個並不是很理解這種用法,從原理上來解釋,由於B是子類指標,雖然被賦予了基類物件地址,但是ptr->foo()在呼叫的時候,由於地址偏移量固定,偏移量是子類物件的偏移量,於是即使在指向了一個基類物件的情況下,還是呼叫到了子類的函式,雖然可能從始到終都沒有子類物件的例項化出現。

  第四個:而ptr->fuu()的呼叫,可能還是因為C++多型性的原因,由於指向的是一個基類物件,通過虛擬函式列表的引用,找到了基類中foo()函式的地址,因此呼叫了基類的函式。由此可見多型性的強大,可以適應各種變化,不論指標是基類的還是子類的,都能找到正確的實現方法。


來源

http://www.cnblogs.com/meteoric_cry/archive/2013/05/08/3067270.html