當乙個函式是內聯和虛函式時,會發生**替換或使用虛表呼叫嗎? 為了弄清楚內聯和虛函式,讓我們將它們分開來考慮。通常,乙個內聯函式是被展開的。
01.
class
cfoo
10.
11.
int
setval(
int
v)
12.
13.
};
這裡,如果使用下列**:
1.
cfoo x;
2.
3.
x.setval(17);
4.
5.
int
y = x.getval();
那麼編譯器產生的目標**將與下面的**段一樣:
1.
cfoo x;
2.
3.
x.val = 17;
4.
5.
int
y = x.val;
你當然不能這麼做,因為val是個私有變數。內聯函式的優點是不用函式呼叫就能隱藏資料,僅此而已。
虛函式有多型性,意味著派生的類能實現相同的函式,但功能卻不同。假設 getval 被宣告為虛函式,並且你有第二個以不同方法實現的類 cfoo2:
01.
class
cfoo2 :
public
cfoo
08.
09.
};
如果 pfoo是乙個 cfoo 或 cfoo2 指標,那麼,無論 pfoo 指向哪個類 cfoo 或 cfoo2,成員函式 pfoo->getval 都能呼叫成功。
如果乙個函式既是虛函式,又是內聯函式,會是什麼情況呢?記住,有兩種方式建立內聯函式,
第一種是在函式定義中使用關鍵字 inline,如:
1.
inline
cfoo::getval()
第二種是在類的宣告中編寫函式體,就象前面的 cfoo2::getval 一樣。所以如果將虛函式體包含在類的宣告中,如:
1.
class
cfoo
6.
7.
};
編譯器便認為這個函式 getval 是內聯的,同時也是虛擬的。那麼,多型性和內聯特性如何同時工作呢?
編譯器遵循的第乙個規則是無論發生什麼事情,多型性必須起作用。如果有乙個指向 cfoo 物件的指標,pfoo->getval 被保證去呼叫正確的函式。一般情況下,這就是說函式 getval 將被例項化為非內聯函式,並有vtable(虛表)入口指向它們。但這並不意味著這個函式不能被擴充套件!再看看下面的**:
1.
cfoo x;
2.
3.
x.setval(17)
4.
5.
int
y = x.getval()
編譯器知道x是 cfoo,而不是cfoo2,因為這個堆物件是被顯式宣告的。x肯定不會是cfoo2。所以展開 setval/getval 內聯是安全的。如果要寫更多的複雜**:
01.
cfoo x;
02.
03.
cfoo* pfoo=&x;
04.
05.
pfoo->setval(17);
06.
07.
int
y = pfoo->getval();
08.
09.
...
10.
11.
cfoo2 x2;
12.
13.
pfoo = &x2;
14.
15.
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 內聯函式的目的是為了減少函式呼叫時間。它是把內聯函式的函式體在編譯器預處理的時候替...
內聯虛函式詳解
當乙個函式是內聯和虛函式時,會發生 替換或使用虛表呼叫嗎?為了弄清楚內聯和虛函式,讓我們將它們分開來考慮。通常,乙個內聯函式是被展開的。class cfoo int setval int v 這裡,如果使用下列 cfoo x x.setval 17 int y x.getval 那麼編譯器產生的目標...
c語言是如何處理函式呼叫的?
1.要編譯的測試 int plus int x,int y int main void 2.main中return語句語法分析 if equal tok,return 2.1 當前token為return,則建立型別為nd return的node。2.2 由於return後面跟著plus 3,4 所...