程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> C++對象模型之虛函數實現原理

C++對象模型之虛函數實現原理

編輯:C++入門知識

在C++中,多態(polymorphism)的意思是,用基類的指針或者引用,尋址出一個派生類對象。而虛函數(virtual member function)是多態的基礎,這也是面向對象編程迷人之處。現在剛好有時間,就寫一下自己對C++在單一繼承情況下如何實現虛函數的膚淺認識。

一旦一個類有一個虛函數,那麼一定會建立一個虛表(virtual table),虛表裡面有所有虛函數的地址。虛表是該類所有對象共享的,每個對象都有一個虛指針(vptr),指向虛表。虛指針的設定與重置,是由類的構造函數與析構函數以及復制運算符(copy assignment)自動完成的。

虛表是虛函數的基礎。表格有很多槽(slot),通常第一個槽位存放的是每一個類所關聯的類型信息(type_info object),用來支持運行時類型識別(RTTI)。

[cpp]
class Demo{ 
public: 
    Demo(); 
    virtual ~Demo(); 
    virtual void f1(); 
    virtual void f2(); 
    void f3(); 
    static void f4(); 
private: 
    static int ival; 
    long lval; 
}; 

class Demo{
public:
 Demo();
 virtual ~Demo();
 virtual void f1();
 virtual void f2();
 void f3();
 static void f4();
private:
 static int ival;
 long lval;
};

\

圖1 Demo類的對象內存布局

每個對象內部都會存儲非靜態成員變量以及虛指針,其它成員都會在對象外部存儲,這些不是這次要討論的內容。

在C++中,虛函數在編譯器間獲知,而且這些虛函數的地址是固定不變的,執行期不可能新增或者被替換。為了找到函數的地址,每一個虛函數都會有一個在虛表裡面的索引號。

如果繼承基類的過程中,派生類決定不改寫某個虛函數,那麼它就會繼承基類這個函數的實體(定義),此時,在這個派生類的虛表中存放的就是基類的該函數。否則,如果派生類重寫了基類的某個虛函數,那麼派生類的虛表中存放的就會是對此虛函數重新定義的實體。如下圖所示。

 

[cpp]
class Base{ 
public: 
    virtual void x(); 
    virtual void y(); 
    int ival; 
}; 
//單一繼承  
class Derived: public Base{ 
public: 
    void x(); 
    virtual void z(); 
    long lval; 
}; 

class Base{
public:
 virtual void x();
 virtual void y();
 int ival;
};
//單一繼承
class Derived: public Base{
public:
 void x();
 virtual void z();
 long lval;
};

\

圖2 單一繼承情況下的虛表

特別要注意的是:

1)如果是繼承基類聲明的虛函數實體,那麼該函數實體就會被拷貝到派生類虛表相對應的槽位上。

2)如果是使用自己的函數實體,也就是改寫基類的虛函數,那麼也必須把這個函數實體地址放到相對應的槽位上。

3)如果是新增加一個虛函數,那麼虛表就會增加一個slot,並把新的虛函數實體放進這裡。


所以,假設對於上面的例子,我們有這樣的用法:

[cpp]
ptr->x(); 

ptr->x();我們不知道ptr是Base還是Dervied指針,也就無法由ptr取得該對象的虛表,但是,我知道每一個x()函數的地址都會放在#1槽位上,所以,編譯器會將該調用轉換成

[cpp]
(*ptr->vptr[1])(ptr); 

(*ptr->vptr[1])(ptr);上面的表達式中,唯一需要在執行期才能知道的東西是,槽位1是哪個類虛表的,一旦知道了,那麼便可調用相應的函數體了。

 

就寫到這。

多重繼承與虛擬繼承下,虛函數的實現就沒那麼容易了。下次有空寫一下。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved