天天看點

inline函數的使用方法



inline關鍵字用來定義一個類的内聯函數,引入它的主要原因是用它替代C中表達式形式的宏定義。

表達式形式的宏定義如下:

#define ExpressionName(Var1,Var2) ((Var1)+(Var2))*((Var1)-(Var2))

為什麼要取代這種形式呢,且聽我道來:

1. 首先談一下在C中使用這種形式宏定義的原因,C語言是一個效率很高的語言,這種宏定義在形式及使用上像一個函數,但它使用預處理器實作,沒有了參數壓棧,代碼生成等一系列的操作,是以,效率很高,這是它在C中被使用的一個主要原因。

2. 這種宏定義在形式上類似于一個函數,但在使用它時,僅僅隻是做預處理器符号表中的簡單替換,是以它不能進行參數有效性的檢測,也就不能享受C++編譯器嚴格類型檢查的好處,另外它的傳回值也不能被強制轉換為可轉換的合适的類型,這樣,它的使用就存在着一系列的隐患和局限性。

3. 在C++中引入了類及類的通路控制,這樣,如果一個操作或者說一個表達式涉及到類的保護成員或私有成員,你就不可能使用這種宏定義來實作(因為無法将this指針放在合适的位置)。

4. inline 推出的目的,也正是為了取代這種表達式形式的宏定義,它消除了宏定義的缺點,同時又很好地繼承了宏定義的優點。

對應于上面的1-3點,闡述如下:

1. inline 定義的類的内聯函數,函數的代碼被放入符号表中,在使用時直接進行替換,(像宏一樣展開),沒有了調用的開銷,效率也很高。

2. 很明顯,類的内聯函數也是一個真正的函數,編譯器在調用一個内聯函數時,會首先檢查它的參數的類型,保證調用正确。然後進行一系列的相關檢查,就像對待任何一個真正的函數一樣。這樣就消除了它的隐患和局限性。

3. inline 可以作為某個類的成員函數,當然就可以在其中使用所在類的保護成員及私有成員。

在何時使用inline函數:

首先,你可以使用inline函數完全取代表達式形式的宏定義。

另外要注意,内聯函數一般隻會用在函數内容非常簡單的時候,這是因為,内聯函數的代碼會在任何調用它的地方展開,如果函數太複雜,代碼膨脹帶來的惡果很可能會大于效率的提高帶來的益處。内聯函數最重要的使用地方是用于類的存取函數。

 

而如下風格的函數Foo 則成為内聯函數 

    是以說,C++ inline函數是一種“用于實作的關鍵字”,而不是一種“用于聲明的關鍵字”。一般地,使用者可以閱讀函數的聲明,但是看不到函數的定義。盡管在大多數教科書中内聯函數的聲明、定義體前面都加了inline 關鍵字,但我認為inline 不應該出現在函數的聲明中。這個細節雖然不會影響函數的功能,但是展現了高品質C++/C 程式設計風格的一個基本原則:聲明與定義不可混為一談,使用者沒有必要、也不應該知道函數是否需要内聯。

定義在類聲明之中的成員函數将自動地成為内聯函數

例如

将成員函數的定義體放在類聲明之中雖然能帶來書寫上的友善,但不是一種良好的程式設計風格,上例應該改成:

C++内聯函數不宜使用情況介紹

C++語言中的各種應用是一個非常高深的内容,其中的文法、特性、标準類庫都是我們值得深入研究的内容。C++内聯函數就是其中一個比較重要的内容。我們在這裡總結了一下C++内聯函數不宜使用的環境。

  内聯能提高函數的執行效率,為什麼不把所有的函數都定義成C++内聯函數?如果所有的函數都是内聯函數,還用得着“内聯”這個關鍵字嗎?内聯是以代碼膨脹(複制)為代價,僅僅省去了函數調用的開銷,進而提高函數的執行效率。如果執行函數體内代碼的時間,相比于函數調用的開銷較大,那麼效率的收獲會很少。另一方面,每一處内聯函數的調用都要複制代碼,将使程式的總代碼量增大,消耗更多的記憶體空間。

  以下情況不适合使用inline函數

  (1)如果函數體内的代碼比較長,使用内聯将導緻記憶體消耗代價較高。

  (2)如果函數體内出現循環,那麼執行函數體内代碼的時間要比函數調用的開銷大。類的構造函數和析構函數容易讓人誤解成使用内聯更有效。要當心構造函數和析構函數可能會隐藏一些行為,如“偷偷地”執行了基類或成員對象的構造函數和析構函數。是以不要随便地将構造函數和析構函數的定義體放在類聲明中。一個好的編譯器将會根據函數的定義體,自動地取消不值得的内聯(這進一步說明了 inline 不應該出現在函數的聲明中)。

  注意點:

  C++内聯函數既能夠去除函數調用所帶來的效率負擔又能夠保留一般函數的優點。然而,内聯函數并不是萬能藥,在一些情況下,它甚至能夠降低程式的性能。是以在使用的時候應該慎重。

  1.我們先來看看内聯函數給我們帶來的好處:從一個使用者的角度來看,C++内聯函數看起來和普通函數一樣,它可以有參數和傳回值,也可以有自己的作用域,然而它卻不會引入一般函數調用所帶來的負擔。另外,它可以比宏更安全更容易調試。

  當然有一點應該意識到,inline specifier僅僅是對編譯器的建議,編譯器有權利忽略這個建議。那麼編譯器是如何決定函數内聯與否呢?一般情況下關鍵性因素包括函數體的大小,是否有局部對象被聲明,函數的複雜性等等。

  2.那麼如果一個函數被聲明為inline但是卻沒有被内聯将會發生什麼呢?理論上,當編譯器拒絕内聯一個函數的時候,那個函數會像普通函數一樣被對待,但是還會出現一些其他的問題。例如下面這段代碼:

 

    因為成員函數Time::Show()包括一個局部變量和一個for循環,是以編譯器一般拒絕inline,并且把它當作一個普通的成員函數。但是這個包含類聲明的頭檔案會被單獨的#include進各個獨立的編譯單元中:

     結果編譯器為這個程式生成了兩個相同成員函數的拷貝:

   

  當程式被連結的時候,linker将會面對兩個相同的Time::Show()拷貝,于是函數重定義的連接配接錯誤發生。但是老一些的C++實作對付這種情況的辦法是通過把一個un-inlined函數當作static來處理。是以每一份函數拷貝僅僅在自己的編譯單元中可見,這樣連結錯誤就解決了,但是在程式中卻會留下多份函數拷貝。在這種情況下,程式的性能不但沒有提升,反而增加了編譯和連結時間以及最終可執行體的大小。但是幸運的是,新的C++标準中關于un-inlined函數的說法已經改變。一個符合标準C++實作應該隻生成一份函數拷貝。然而,要想所有的編譯器都支援這一點可能還需要很長時間。

  另外關于C++内聯函數還有兩個更令人頭疼的問題。第一個問題是該如何進行維護。一個函數開始的時候可能以内聯的形式出現,但是随着系統的擴充,函數體可能要求添加額外的功能,結果内聯函數就變得不太可能,是以需要把inline specifier去除以及把函數體放到一個單獨的源檔案中。另一個問題是當内聯函數被應用在代碼庫的時候産生。當内聯函數改變的時候,使用者必須重新編譯他們的代碼以反映這種改變。然而對于一個非内聯函數,使用者僅僅需要重新連結就可以了。

  這裡想要說的是,内聯函數并不是一個增強性能的靈丹妙藥。隻有當函數非常短小的時候它才能得到我們想要的效果,但是如果函數并不是很短而且在很多地方都被調用的話,那麼将會使得可執行體的體積增大。最令人煩惱的還是當編譯器拒絕内聯的時候。在老的實作中,結果很不盡人意,雖然在新的實作中有很大的改善,但是仍然還是不那麼完善的。一些編譯器能夠足夠的聰明來指出哪些函數可以内聯哪些不能,但是,大多數編譯器就不那麼聰明了,是以這就需要我們的經驗來判斷。如果C++内聯函數不能增強行能,就避免使用它!