inline内联的用法与作用
生活随笔
收集整理的這篇文章主要介紹了
inline内联的用法与作用
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
inline內聯的用法與作用
? ? ?內聯函數是一種編譯機制,優點從代碼上是看不出來的,但是程序的執行效率上有差別,通常,編譯器對函數調用的處理是一種類似中斷的方式,即當執行到函數調用語句時,程序把當前所有的狀態信息比如CPU所有寄存器(其中一個很重要的就是指令指針寄存器)的值保存起來,然后放心大膽地轉去執行那個函數的代碼,執行完后再返回原來的地方,恢復原先保存過的狀態信息,于是也就可以接著原來被中斷的指令繼續往下執行。這樣,就很容易實現代碼的結構化,因為可以把一些獨立的功能模塊寫成函數,函數內部的變量和外部的變量互不影響,而且函數執行完后就可以釋放這個函數內部變量的所使用的內存空間(這就是為什么函數退出后,其內部變量不再有效),對內存的使用也是很經濟的(否則,如果一個大的程序全部由一個函數組成,那么所有的變量都得自始至終地占用內存空間),當然,還有其他優點,比如可以實現遞歸,總之是好處多多。 ?? ? ?可是,任何事情往往都有兩方面,這樣做雖然好處多多,但也是有代價的,那就是前面所說的,任何一次函數調用,程序都得進行保存和恢復狀態信息的動作,用數據結構的術語說就是進棧和退棧,當然,還有內存分配的過程,如果函數的代碼非常少,這種代價并不是可忽略的,比如說,你編寫一個類,里面有個記錄狀態的成員變量: ?
Class MyClass { private: int m_iState; } ? ? 按照面向對象的思想,函數的屬性應盡量的私有化,但外部怎么獲得這個屬性值呢?一般的方法就是加一個共有函數,這就實現的面向對象思想中所謂“通過公用接口操作對象的私有屬性”。于是就變成了: ? Class MyClass { public: int GetState(); private: int m_iState; } int MyClass::GetState() { return m_iState; } ? ? 這樣一來,面向對象思想倒是體現出來了,但你的CPU會恨你:“你丫的,一個鳥樣小的函數就返回一個整數卻讓老子進一次棧、彈一次棧”,內存也會憋屈的說:“兄弟,老子也得跟著分配內存啊!” ?
? ? 但對你來說,也很委屈,怎么辦,把所有的屬性都改成public?讓外部內碼直接訪問?況且,那樣也不解決所有問題,因為有時候即使不是為了面向對象,我們也需要把獨立的功能模塊做成函數,比如說產生隨機數的函數。我想 ?
int iRand=rand(); ?
總比: ?
int iRand=((int)(MULTIPLIER * Seed + INCREMENT)>>16)&0x7fff; ?
? ? 看起來舒服吧?(我這里只是打個比方,VC的rand函數并不是內聯函數) ?
? ? 而內聯函數就是解決這個問題了,對于程序員,他還是把獨立功能寫成函數的形式,但只要聲明為內聯,編譯器就不把它編譯成一次函數調用,而只是類似于把函數的代碼拷貝到被調用的地方,而且這完全是編譯器私下里完成的,原來的訪問權限等問題絲毫不受影響。這不是兩全齊美了嗎:在保證代碼的面向對象性和結構化不受損失的條件下,程序的效率也沒有損失,比如上面那個類,就變成了: ?
Class MyClass { public: inline int GetState(); private: int m_iState; } int inline MyClass::GetState() { return m_iState; } ? ? 有一點要注意,內聯函數要跟類的聲明寫在同一個文件中,否則編譯會出錯。按照VC管理源文件的風格來說,就是內聯函數最好寫在聲明類的.h文件中,而不是像一般函數那樣寫在實現類的.cpp文件中。 ?
? ? 當然,內聯函數還有另外一種寫法,就是直接寫在類中,此時,不必使用“inline”關鍵字。 ?
Class MyClass { public: int GetState(){ return m_iState; } private: int m_iState; } ? ? 最后,還要注意,內聯函數只是一種編譯機制,用上面兩種形式聲明的函數僅僅是建議編譯器進行內聯,而編譯器是否內聯不一定。正如前面所說,函數調用的開銷只是對小的函數不可忽略,對于重量級的函數還是可以忽略的,而且在絕大多數的場合,函數調用才是人間正道,才是解決問題的最佳。所以大多數編譯器并不把帶有循環、遞歸等或者代碼比較多的函數進行內聯編譯,有的甚至不允許聲明成內聯的。 ? ?
? ? 在解決C + +中宏存取私有的類成員的問題過程中,所有和預處理器宏有關的問題也隨著消失了。這是通過使宏被編譯器控制來實現的。在 C + +中,宏的概念是作為內聯函數來實現的,而內聯函數無論在任何意義上都是真正的函數。唯一不同之處是內聯函數在適當時像宏一樣展開,所以函數調用的開銷被取消。因此,應該永遠不使用宏,只使用內聯函數。 ?
? ? 任何在類中定義的函數自動地成為內聯函數,但也可以使用i n l i n e關鍵字放在類外定義的函數前面使之成為內聯函數。但為了使之有效,必須使函數體和聲明結合在一起,否則,編譯器將它作為普通函數對待。因此 ?
inline int PlusOne(int x); ? ? 沒有任何效果,僅僅只是聲明函數(這不一定能夠在稍后某個時候得到一個內聯定義)。成功的方法如下: ?
inline int PlusOne(int x) { return ++x ;} ? ? 注意,編譯器將檢查函數參數列表使用是否正確,并返回值(進行必要的轉換)。這些事情是預處理器無法完成的。假如對于上面的內聯函數,我們寫成一個預處理器宏的話,將有不想要的副作用。 ?
? ? 一般應該把內聯定義放在頭文件里。當編譯器看到這個定義時,它把函數類型(函數名+ ?返回值)和函數體放到符號表里。當使用函數時,編譯器檢查以確保調用是正確的且返回值被 ?正確使用,然后將函數調用替換為函數體,因而消除了開銷。內聯代碼的確占用空間,但假如函數較小,這實際上比為了一個普通函數調用而產生的代碼(參數壓棧和執行C A L L)占用的空間還少。在頭文件里,內聯函數默認為內部連接——即它是static, 并且只能在它被包含的編譯單元看到。因而,只要它們不在相同的編譯單元中聲明,在內聯函數和全局函數之間用同樣的名字也不會在連接時產生沖突。 ?
? ? 為了定義內聯函數,通常必須在函數定義前面放一個i n l i n e關鍵字。但這在類內部定義內聯函數時并不是必須的。任何在類內部定義的函數自動地為內聯函數。如下例: ?
#include <iostream.h> class point{ private: int i,j,k; public: point() {i=j=k=0; } point(int I,int J,int K) { i=I; j=J k=K; } void print(const char* msg="") const{ if(*msg) cout<<"msg"<<endl; cout<<"i="<<i<<endl; cout<<"j="<<j<<endl; cout<<"k="<<k<<endl; } }; main(){ point p,q(1,2,3); p.print("value of p"); q.print("value of q"); } ? ? 當然,因為類內部的內聯函數節省了在外部定義成員函數的額外步驟,所以我們一定想在類聲明內每一處都使用內聯函數。但應記住,內聯的目的是減少函數調用的開銷。假如函數較大,那么花費在函數體內的時間相對于進出函數的時間的比例就會較大,所以收獲會較小。而且內聯一個大函數將會使該函數所有被調用的地方都做代碼復制,結果代碼膨脹而在速度方面獲得的好處卻很少或者沒有。
總結
以上是生活随笔為你收集整理的inline内联的用法与作用的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: C/C++语言宏定义##连接符和符#的使
- 下一篇: JAVA 内存泄露详解(原因、例子及解决