內聯虛函式詳解

2021-04-15 10:07:45 字數 2235 閱讀 1727

當乙個函式是內聯和虛函式時,會發生**替換或使用虛表呼叫嗎? 為了弄清楚內聯和虛函式,讓我們將它們分開來考慮。通常,乙個內聯函式是被展開的。

class cfoo

int setval(int v)

};        這裡,如果使用下列**:

cfoo x;

x.setval(17);

int y = x.getval();  那麼編譯器產生的目標**將與下面的**段一樣:

cfoo x;

x.val = 17;

int y = x.val;  你當然不能這麼做,因為val是個私有變數。內聯函式的優點是不用函式呼叫就能隱藏資料,僅此而已。

虛函式有多型性,意味著派生的類能實現相同的函式,但功能卻不同。假設 getval 被宣告為虛函式,並且你有第二個 以不同方法實現的類 cfoo2:

class cfoo2 : public cfoo

};如果 pfoo是乙個 cfoo 或 cfoo2 指標,那麼,無論 pfoo 指向哪個類 cfoo 或 cfoo2,成員函式 pfoo->getval 都能呼叫成功。

如果乙個函式既是虛函式,又是內聯函式,會是什麼情況呢?記住,有兩種方式建立內聯函式,

第一種是在函式定義中使用關鍵字 inline,如:

inline cfoo::getval()       

第二種是在類的宣告中編寫函式體,就象前面的 cfoo2::getval 一樣。所以如果將虛函式體包含在類的宣告中,如:

class cfoo

};        編譯器便認為這個函式 getval 是內聯的,同時也是虛擬的。那麼,多型性和內聯特性如何同時工作呢?

編譯器遵循的第乙個規則是無論發生什麼事情,多型性必須起作用。如果有乙個指向 cfoo 物件的指標,pfoo->getval 被保證去呼叫正確的函式。一般情況下,這就是說函式 getval 將被例項化為非內聯函式,並有vtable(虛表)入口指向它們。但這並不意味著這個函式不能被擴充套件!再看看下面的**:

cfoo x;

x.setval(17)

int y = x.getval()  編譯器知道x是 cfoo,而不是cfoo2,因為這個堆物件是被顯式宣告的。x肯定不會是cfoo2。所以展開 setval/getval 內聯是安全的。如果要寫更多的複雜**:

cfoo x;

cfoo* pfoo=&x;

pfoo->setval(17);

int y = pfoo->getval();

cfoo2 x2;

pfoo = &x2;

pfoo->setval(17); //etc. 編譯器知道 pfoo 第一次指向x,第二次指向x2,所以展開虛函式也是安全的。

你還可以編寫更複雜的**,其中,pfoo 所指的物件型別總是透明的,但是大多數編譯器不會做任何更多的分析。即使在前面的例子中,某些編譯器將會安全執行,例項化並通過乙個虛表來呼叫。實際上,編譯器總是忽略內聯需要並總是使用虛表。唯一絕對的規則是**必須工作;也就是說,虛函式必須有多型行為。

通常,無論是顯式還是隱式內聯,它只是乙個提示而已,並非是必須的,就象暫存器一樣。編譯器完全能拒絕展開乙個非虛內聯函式,c++編譯器常常首先會報錯:「內聯中斷-函式太大」。如果內聯函式呼叫自身,或者你在某處傳遞其位址,編譯器必須產生乙個正常(外聯?)函式。內聯函式在debug builds中不被展開,可設定編譯選項來預防。

要想知道編譯器正在做什麼,唯一的方法是看它產生的**。對於微軟的編譯器來說,你可以用-fa編譯選項產生彙編清單。你不必知道匯程式設計序如何做。我鼓勵你完成這個實驗;這對於了解機器實際所做的事情機器有益,同時你可學習許多彙編列表中的內容。

有關內聯函式的東西比你第一次接觸它時要複雜得多。有許多種情況強迫編譯器產生正常函式:遞迴,獲取函式位址,太大的那些函式和虛函式。但是如果編譯器決定例項化你的內聯函式,就要考慮把函式放在什麼地方?它進入哪個模組?

通常類在標頭檔案中宣告,所以如果某個cpp包含foo.h,並且編譯器決定例項化cfoo::getval,則在cpp檔案中將它例項化成乙個靜態函式。如果十個模組包含foo.h,編譯器產生的虛函式拷貝就有十個。實際上,可以用虛表指向不同型別的getval拷貝,從而是相同型別的物件只產生拷貝。一些鏈結器能巧妙地在鏈結時排除冗餘,但一般你是不能指望他來保證的。

我們得出的結論是:最好不要使用內聯虛函式,因為它們幾乎不會被展開,即便你的函式只有一行,你最好還是將它與其它的類函式一起放在模組(cpp檔案)中。當然,開發者常常將簡短的虛函式放在類宣告中-不是因為他們希望這個函式被展開為內聯,而是因為這樣做更方便和可讀性更強。 

內聯函式 虛函式

在c 中,inline關鍵字和virtual關鍵字分別用來定義c 中的內聯函式和虛函式,他們在各自的場合都有其各自的應用,下面將簡單介紹他們各自的功能,然後在說明為什麼乙個函式不能同時是虛函式和內聯函式 inline 內聯函式的目的是為了減少函式呼叫時間。它是把內聯函式的函式體在編譯器預處理的時候替...

虛函式能否是內聯函式

內聯函式是在編譯時,將呼叫函式處插入內聯函式的 省去了函式呼叫時的開銷。虛函式是通過指標或引用呼叫函式時,通過虛函式表來確定呼叫的函式,在執行時確定。那麼虛函式是否可以是內聯函式?以前沒有想過這個問題,表面上看,虛函式不能為內聯函式。我們在類中定義的函式都是內聯函式,析構函式經常在類中定義,而析構函...

虛函式能否是內聯函式

內聯函式是在編譯時,將呼叫函式處插入內聯函式的 省去了函式呼叫時的開銷。虛函式是通過指標或引用呼叫函式時,通過虛函式表來確定呼叫的函式,在執行時確定。那麼虛函式是否可以是內聯函式?以前沒有想過這個問題,表面上看,虛函式不能為內聯函式。我們在類中定義的函式都是內聯函式,析構函式經常在類中定義,而析構函...