国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

C++如何處理內聯虛函數

2019-11-17 05:44:25
字體:
來源:轉載
供稿:網友
    當一個函數是內聯和虛函數時,會發生代碼替換或使用虛表調用嗎? 為了弄清楚內聯和虛函數,讓我們將它們分開來考慮。通常,一個內聯函數是被展開的。 class CFoo { PRivate: int val; public: int GetVal() { return val; } int SetVal(int v) { return val=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 { public:  // virtual in base class too! virtual int CFoo2::GetVal() { return someOtherVal; }  };     假如 pFoo是一個 CFoo 或 CFoo2 指針,那么,無論 pFoo 指向哪個類 CFoo 或 CFoo2,成員函數 pFoo->GetVal 都能調用成功。

    假如一個函數既是虛擬函數,又是內聯函數,會是什么情況呢?記住,有兩種方式建立內聯函數,

第一種是在函數定義中使用要害字 inline,如: inline CFoo::GetVal() { return val; } 第二種是在類的聲明中編寫函數體,就象前面的 CFoo2::GetVal 一樣。所以假如將虛函數體包含在類的聲明中,如: class CFoo { public: virtual int GetVal() { return val; } };     編譯器便認為這個函數 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文件)中。當然,開發者經常將簡短的虛函數放在類聲明中-不是因為他們希望這個函數被展開為內聯,而是因為這樣做更方便和可讀性更強。

發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 德安县| 通道| 蓬溪县| 泰来县| 阳新县| 南京市| 瑞金市| 徐汇区| 东港市| 新晃| 玉山县| 同心县| 余姚市| 高平市| 东至县| 平舆县| 渭南市| 清河县| 玉树县| 库伦旗| 土默特左旗| 黎城县| 汉源县| 河池市| 开鲁县| 武鸣县| 宜阳县| 和林格尔县| 嘉义市| 富民县| 衡阳市| 顺昌县| 丘北县| 依兰县| 德江县| 九龙坡区| 长丰县| 渭南市| 乐至县| 襄樊市| 浏阳市|