C 虛指標實現及效率

2021-06-05 09:50:33 字數 2167 閱讀 1774

單繼承虛函式例子

class point

virtual

float y() const

virtual

float z() const

// ...

protected:

point( float x = 0.0 );

float _x; };

class point2d : public point

~point2d();

// overridden base class virtual functions

point2d& mult( float );

float y() const

// ... other operations ...

protected:

float _y; };

class point3d: public point2d

~point3d();

// overridden base class virtual functions

point3d& mult( float );

float z() const

// ... other operations ...

protected:

float _z; };

vtable和vptr結構

虛函式的實現是通過vtable和vptr。每乙個帶有虛函式的類都有乙個vtable,在編譯器生成,每乙個帶有虛函式的類例項都有乙個vptr,該類例項vptr指向該類的vtable,在執行期生成。

如圖左部的類例項記憶體結構,編譯器為之生成__vptr__point的指標,指向該類的vtable。

vtable的結構是乙個函式指標陣列,陣列的每個元素是乙個函式指標,指向該類虛函式的位址。因為基類point的point::mult()為純虛函式,因此point對應的mult函式指標指向乙個pure_virtual_called(),丟擲呼叫純虛函式錯誤。

如圖vtable所示,point類和其子類的析構函式均在vtable[1],mult在vtable[2],y在vtable[3],z在vtable[4]。如果point2d增加point2d自己的虛函式,同時point3d繼承point2d的虛函式,他們相同的虛函式介面同樣對應於相同的vtable陣列下標,如vtable[5],此由編譯器保證,因而編譯器對於虛函式介面能將其轉換為函式指標陣列的下標。

故,當呼叫

ptr->z();

編譯器實際呼叫的是:

( *ptr->vptr[ 4 ] )( ptr );

從而可以找到ptr實際指向的vtable中的虛函式呼叫位址。

虛函式系統開銷

為了實現虛函式,編譯器產生的操作包括:

編譯期,為每乙個類增加乙個vtable函式指標陣列,並使其指向正確的虛函式實現。

執行期,在類的建構函式中,為每乙個類例項增加乙個vptr,指向該類的vtable。

編譯器,將虛函式呼叫編譯為函式指標的呼叫。

執行期,在虛函式呼叫時,通過指向vtable和呼叫函式的index,查詢函式指標(查詢效率為陣列隨機訪問,常數時間),呼叫虛函式。

由分析得,虛函式開銷主要在編譯期的vtable函式指標陣列的構造,而執行期的函式指標查詢不是效能瓶頸。同時,乙個帶虛函式的基類無論有多少個孩子類,並不會降低虛函式效能,而如果類的繼承層次太深,底層類例項的建構函式則需要為類繼承層次的每一層父類初始化vptr,效率降低。

虛函式系統效能測試

void

cross_product( const pt3d &pa, const pt3d &pb )

main()

optimized non-optimized

inline member 0.08 4.70

nonstatic member 4.43 6.13

virtual member

cc 4.76 6.90

ncc 4.63 7.72

cc和ncc是比較的兩個編譯器版本,對於上述計算函式的

測試,虛函式的呼叫開銷主要是3.4虛表查詢,

虛函式呼叫損失了

4% 到

11%的執行時間。相對於io操作,可以忽略。

參考文獻

inside the c++ object model, by stanley b. lippman

C 虛指標實現及效率

單繼承虛函式例子 classpoint virtual float y const virtual float z const protected point float x 0.0 float x classpoint2d public point point2d overridden base ...

C 虛函式實現 虛函式表 虛表指標

內聯函式 編譯時展開的,虛函式是為了實現多型,是動態行為,兩者是矛盾的。內聯函式展開就不存在了,也就不存在函式位址了,無法呼叫。但是,由於inline只是向編譯器建議,所以編譯器不會讓inline和virtual同時起作用,所以也不會報錯。建構函式 虛函式使用虛指標呼叫函式,呼叫建構函式之前沒有構造...

c 虛函式實現與this指標

我們知道當我們sizeof 乙個類的時候,類的成員函式是不計算在物件的大小的裡的,這是為什麼呢?因為類的成員函式不是屬於某乙個物件的,而是類的所有物件所共享的,就像static變數那樣。如果虛函式和普通成員函式一樣,那麼我們就不能通過指向子類的基類指標來引用子類的方法了,因為我們將不知道呼叫哪個方法...