C 語法 深度剖析C 中的inline函式

2021-10-10 23:42:57 字數 2598 閱讀 5459

目錄

1.inline函式的愛恨兩難

3.inline函式與虛函式的愛恨糾纏

4.糟糕的內聯選擇:建構函式與析構函式

5.總結

內聯函式比巨集優點好很多,詳細原因請參見盡量以const、enum、inline替換#define。呼叫內聯函式不需要承受函式呼叫所導致的額外記憶體開銷,編譯器最優化機制通常被設計用來濃縮那些「不含函式呼叫」的**,所以當乙個函式為內聯函式時,或許編譯器就因此有內聯對其執行語境相關的最優化。

「世界上沒有免費的午餐」,內聯函式背後的整體思想是:對乙個函式的呼叫都以函式本體替換它,但這同時會增加你的目標碼大小。在乙個記憶體有限的機器上,過度使用內聯函式會造成程式體積太大。即使用有虛擬記憶體,內聯函式造成的**膨脹也會導致額外的換頁行為,降低指令快取記憶體器的命中率。

另一方面來說,如果內聯函式的本體很小,編譯器針對函式本體所產生的碼可能比函式呼叫所產生的碼更小。因此,將函式設定為inline,確實導致更小的目標碼和較高的命中率。

inline只是對編譯器的乙個申請建議,不是強制命令,編譯器可以選擇對你的建議置之不理。這項申請可以隱式宣告也可以顯式宣告,隱式宣告是將函式定義在類的內部,如下所示:

class person   // 隱式的內聯函式

private:

int mage;

};

顯式宣告的方法則是在函式定義式前面加上關鍵字inline,如下面函式模板:

templateinline const t& max(const t& a, const t& b)
上述顯式的宣告方法中,值得注意的一點是:inline函式和函式模板通常都被定義於標頭檔案中。但是,函式模板未必一定是內聯的!!!inline函式通常一定被放置在標頭檔案中,因為大多數構建環境在編譯過程中進行內聯動作,為了將乙個函式呼叫替換為被呼叫函式的本體,編譯器必須知道那個函式是啥樣。內聯動作在大多數c++程式中是編譯期的行為。函式模板通常也被放置在標頭檔案中,因為它一旦被使用,編譯器為了將它具體化,需要知道它的樣子。某些構建環境中,可以在連線期執行模板具體化,只不過編譯期完成具體化的動作比較常見。

大部分編譯器拒絕將太複雜(帶有迴圈或遞迴、switch-case語句)的函式設定為inline,而且所有對虛函式的呼叫也都會使inline失效。這是因為虛函式是直到執行期才會確定呼叫哪個函式,而inline函式意味著執行前先把呼叫動作替換為被呼叫函式的本體,編譯器工作在編譯期。如果編譯器都不知道該呼叫哪個函式,你也就明白了為啥虛函式不建議設定為inline。更多詳情原因請看這裡

不要被你的眼睛所欺騙,下面子類建構函式真的是空的嗎???

class base;

class derived: public base  // derived建構函式真的是空的嗎???

// ...

private:

string dm1, dm2, dm3;

};

當你使用new,動態建立的物件被其建構函式自動初始化;當你使用delete,對應的析構函式會被呼叫。當你建立乙個物件時,基類及其每個成員變數都會被自動構造;當你銷毀乙個物件時,從子類開始執行析構動作。如果有個異常在物件構造期間丟擲,該物件已構造好的那一部分會被自動銷毀。你的程式內一定有某些**讓那些事情發生,這些**即編譯器在編譯期間產生並安排在你**中的某個地方。有時候,可能就存在於你的建構函式和析構函式中。上面表面上看起來空的derived建構函式所產生的**,如下所示:

derived::derived()

catch(...)

trycatch(...)

trycatch(...)

}

上面的**並不能代表編譯器真正產生的**,因為真正的編譯器會以更複雜的做法來處理異常。儘管如此,上面的**已經能反映derived的空白建構函式必須提供的行為。不論編譯器在其內部所做的異常處理有多麼複雜,derived建構函式至少一定會陸續呼叫成員變數和基類兩者的建構函式,而那些呼叫會影響編譯器是否對此空白函式執行內聯動作。

最後,程式庫的設計者必須知道:內聯函式無法隨著程式庫的公升級而公升級。如果fun()是程式庫中的乙個內聯函式,客戶將fun()本體編進其程式中,一旦程式庫設計者決定改變fun(),所有用到函式f()的客戶端程式都必須重新編譯。如果fun()是non-inline函式,一旦它有所修改客戶端只需要重新連線即可。

對程式開發而言,大部分偵錯程式對內聯函式都束手無策,因為你無法在乙個並不存在的函式內部設定斷點進行除錯呢?哪些函式可以設定為inline呢?我的建議是:首先不要將任何函式宣告為inline,或者將inline實施範圍侷限在那些一定可以成為inline的場合。

(1) 將大多數inline行為限制在小型、被頻繁呼叫的函式身上。這可以使以後的除錯過程和二進位制公升級更容易,也可以使潛在的**膨脹問題最小化。

(2) 不要只是因為函式模板出現在標頭檔案中,就將它們宣告為inline。

C 多型深度剖析

測試環境 target x86 64 linux gnu gcc version 5.3.1 20160413 ubuntu 5.3.1 14ubuntu2.1 多型一詞最初 於希臘語,意思是具有多種形式或形態的情形,當然這只是字面意思,它在c 語言中多型有著更廣泛的含義。這要先從物件的型別說起!物...

C語言深度剖析

c語言深度剖析 1,編譯器通常不為普通const唯讀變數分配儲存空間,而是將他們儲存在符號表中,使得它成為乙個編譯期間的值,沒有了儲存與讀記憶體的操作,使得它的效率更高。2,const int p p可變,p指向的物件不變。int const p p可變,p指向的物件不可變 int const p ...

C語言深度剖析

什麼是資料型別?資料型別是建立變數的模具 資料型別是建立變數的模具 型別的本質 資料型別可理解為固定記憶體大小的別名 資料型別的本質是固定記憶體大小的別名 變數本質 變數是一段實際連續儲存空間的別名 型別和變數的關係 vim program.c include int main gcc progra...