自己對 inline 函數(shù)做個總結
(一)inline函數(shù)(摘自C++ Primer的第三版)
?????????在函數(shù)聲明或定義中函數(shù)返回類型前加上關鍵字inline即把min()指定為內聯(lián)。
?? inline int min(int first, int secend) {/****/};
??????inline函數(shù)對編譯器而言必須是可見的,以便它能夠在調用點內展開該函數(shù)。與非inline函數(shù)不同的是,inline函數(shù)必須在調用該函數(shù)的每個文本文件中定義。當然,對于同一程序的不同文件,如果inline函數(shù)出現(xiàn)的話,其定義必須相同。對于由兩個文件compute.C和draw.C構成的程序來說,程序員不能定義這樣的min()函數(shù),它在compute.C中指一件事情,而在draw.C中指另外一件事情。如果兩個定義不相同,程序將會有未定義的行為:
??????為保證不會發(fā)生這樣的事情,建議把inline函數(shù)的定義放到頭文件中。在每個調用該inline函數(shù)的文件中包含該頭文件。這種方法保證對每個inline函數(shù)只有一個定義,且程序員無需復制代碼,并且不可能在程序的生命期中引起無意的不匹配的事情。
(二)內聯(lián)函數(shù)的編程風格(摘自高質量C++/C 編程指南)
關鍵字inline 必須與函數(shù)定義體放在一起才能使函數(shù)成為內聯(lián),僅將inline 放在函數(shù)聲明前面不起任何作用。
如下風格的函數(shù)Foo 不能成為內聯(lián)函數(shù):inline void Foo(int x, int y); // inline 僅與函數(shù)聲明放在一起void Foo(int x, int y){}而如下風格的函數(shù)Foo 則成為內聯(lián)函數(shù):void Foo(int x, int y);inline void Foo(int x, int y) // inline 與函數(shù)定義體放在一起{}所以說,inline 是一種“用于實現(xiàn)的關鍵字”,而不是一種“用于聲明的關鍵字”。一般地,用戶可以閱讀函數(shù)的聲明,但是看不到函數(shù)的定義。盡管在大多數(shù)教科書中內聯(lián)函數(shù)的聲明、定義體前面都加了inline 關鍵字,但我認為inline 不應該出現(xiàn)在函數(shù)的聲明中。這個細節(jié)雖然不會影響函數(shù)的功能,但是體現(xiàn)了高質量C++/C 程序設計風格的一個基本原則:聲明與定義不可混為一談,用戶沒有必要、也不應該知道函數(shù)是否需要內聯(lián)。
定義在類聲明之中的成員函數(shù)將自動地成為內聯(lián)函數(shù),例如class A{public:void Foo(int x, int y) {? } // 自動地成為內聯(lián)函數(shù)}將成員函數(shù)的定義體放在類聲明之中雖然能帶來書寫上的方便,但不是一種良好的編程風格,上例應該改成:// 頭文件class A{public:void Foo(int x, int y);}// 定義文件inline void A::Foo(int x, int y){}?慎用內聯(lián)內聯(lián)能提高函數(shù)的執(zhí)行效率,為什么不把所有的函數(shù)都定義成內聯(lián)函數(shù)?如果所有的函數(shù)都是內聯(lián)函數(shù),還用得著“內聯(lián)”這個關鍵字嗎?內聯(lián)是以代碼膨脹(復制)為代價,僅僅省去了函數(shù)調用的開銷,從而提高函數(shù)的執(zhí)行效率。如果執(zhí)行函數(shù)體內代碼的時間,相比于函數(shù)調用的開銷較大,那么效率的收獲會很少。另一方面,每一處內聯(lián)函數(shù)的調用都要復制代碼,將使程序的總代碼量增大,消耗更多的內存空間。以下情況不宜使用內聯(lián):(1)如果函數(shù)體內的代碼比較長,使用內聯(lián)將導致內存消耗代價較高。(2)如果函數(shù)體內出現(xiàn)循環(huán),那么執(zhí)行函數(shù)體內代碼的時間要比函數(shù)調用的開銷大。類的構造函數(shù)和析構函數(shù)容易讓人誤解成使用內聯(lián)更有效。要當心構造函數(shù)和析構函數(shù)可能會隱藏一些行為,如“偷偷地”執(zhí)行了基類或成員對象的構造函數(shù)和析構函數(shù)。所以不要隨便地將構造函數(shù)和析構函數(shù)的定義體放在類聲明中。一個好的編譯器將會根據函數(shù)的定義體,自動地取消不值得的內聯(lián)(這進一步說明了inline 不應該出現(xiàn)在函數(shù)的聲明中)。
注意點:
內聯(lián)函數(shù)既能夠去除函數(shù)調用所帶來的效率負擔又能夠保留一般函數(shù)的優(yōu)點。然而,內聯(lián)函數(shù)并不是萬能藥, ?
? 在一些情況下,它甚至能夠降低程序的性能。因此在使用的時候應該慎重。 ?
? ? ? ? ? 1.我們先來看看內聯(lián)函數(shù)給我們帶來的好處:從一個用戶的角度來看,內聯(lián)函數(shù)看起來和普通函數(shù)一樣, ?
? 它可以有參數(shù)和返回值,也可以有自己的作用域,然而它卻不會引入一般函數(shù)調用所帶來的負擔。另外, ?
? 它可以比宏更安全更容易調試。 ?
? ? 當然有一點應該意識到,inline ? specifier僅僅是對編譯器的建議,編譯器有權利忽略這個建議。那么編譯器是 ?
? ? 如何決定函數(shù)內聯(lián)與否呢?一般情況下關鍵性因素包括函數(shù)體的大小,是否有局部對象被聲明,函數(shù)的復雜性等等。 ?
? ? ? ? ? 2.那么如果一個函數(shù)被聲明為inline但是卻沒有被內聯(lián)將會發(fā)生什么呢?理論上,當編譯器拒絕內聯(lián)一個 ?
? 函數(shù)的時候,那個函數(shù)會像普通函數(shù)一樣被對待,但是還會出現(xiàn)一些其他的問題。例如下面這段代碼: ?
? // ? filename ? Time.h ?
? #include<ctime> ?
? #include<iostream> ?
? using ? namespace ? std; ?
? class ? Time ?
? { ?
? public: ?
? ? ? ? ? inline ? void ? Show() ? { ? for ? (int ? i ? = ? 0; ? i<10; ? i++) ? cout<<time(0)<<endl;} ?
? }; ?
? ? ? ? ? 因為成員函數(shù)Time::Show()包括一個局部變量和一個for循環(huán),所以編譯器一般拒絕inline,并且把它當作一個普通的成員函數(shù)。但是這個包含類聲明的頭文件會被單獨的#include進各個獨立的編譯單元中: ?
? ? ? ? ? // ? filename ? f1.cpp ?
? #include ? "Time.hj" ?
? void ? f1() ?
? { ?
? ? ? ? ? Time ? t1; ?
? ? ? ? ? t1.Show(); ?
? } ?
? ?
? // ? filename ? f2.cpp ?
? #include ? "Time.h" ?
? void ? f2() ?
? { ?
? ? ? ? ? Time ? t2; ?
? ? ? ? ? t2.Show(); ?
? } ?
? 結果編譯器為這個程序生成了兩個相同成員函數(shù)的拷貝: ?
? void ? f1(); ?
? void ? f2(); ?
? int ? main() ?
? { ?
? ? ? ? ? f1(); ? ?
? ? ? ? ? f2(); ?
? ? ? return ? 0; ?
? } ?
? ?
? 當程序被鏈接的時候,linker將會面對兩個相同的Time::Show()拷貝,于是函數(shù)重 ?
? 定義的連接錯誤發(fā)生。但是老一些的C++實現(xiàn)對付這種情況的辦法是通過把一個 ?
? un-inlined函數(shù)當作static來處理。因此每一份函數(shù)拷貝僅僅在自己的編譯單元中 ?
? 可見,這樣鏈接錯誤就解決了,但是在程序中卻會留下多份函數(shù)拷貝。在這種情況 ?
? 下,程序的性能不但沒有提升,反而增加了編譯和鏈接時間以及最終可執(zhí)行體的大小。 ?
? 但是幸運的是,新的C++標準中關于un-inlined函數(shù)的說法已經改變。一個符合標準 ?
? C++實現(xiàn)應該只生成一份函數(shù)拷貝。然而,要想所有的編譯器都支持這一點可能還 ?
? 需要很長時間。 ?
? 另外關于內聯(lián)函數(shù)還有兩個更令人頭疼的問題。第一個問題是該如何進行維護。 ?
? 一個函數(shù)開始的時候可能以內聯(lián)的形式出現(xiàn),但是隨著系統(tǒng)的擴展,函數(shù)體可能要 ?
? 求添加額外的功能,結果內聯(lián)函數(shù)就變得不太可能,因此需要把inline ? specifier ?
? 去除以及把函數(shù)體放到一個單獨的源文件中。另一個問題是當內聯(lián)函數(shù)被應用在代 ?
? 碼庫的時候產生。當內聯(lián)函數(shù)改變的時候,用戶必須重新編譯他們的代碼以反映這 ?
? 種改變。然而對于一個非內聯(lián)函數(shù),用戶僅僅需要重新鏈接就可以了。 ?
? 這里想要說的是,內聯(lián)函數(shù)并不是一個增強性能的靈丹妙藥。只有當函數(shù)非常短小 ?
? 的時候它才能得到我們想要的效果,但是如果函數(shù)并不是很短而且在很多地方都被 ?
? 調用的話,那么將會使得可執(zhí)行體的體積增大。最令人煩惱的還是當編譯器拒絕內聯(lián) ?
? 的時候。在老的實現(xiàn)中,結果很不盡人意,雖然在新的實現(xiàn)中有很大的改善,但是 ?
? 仍然還是不那么完善的。一些編譯器能夠足夠的聰明來指出哪些函數(shù)可以內聯(lián)哪些 ?
? 不能,但是,大多數(shù)編譯器就不那么聰明了,因此這就需要我們的經驗來判斷。 ?
? 如果內聯(lián)函數(shù)不能增強行能,就避免使用它! ?
? */ ?
? ?
? /* ?
? 用指針代替數(shù)組 ? ?
? 在許多種情況下,可以用指針運算代替數(shù)組索引,這樣做常常能產生又快又短的 ?
? 代碼。與數(shù)組索引相比,指針一般能使代碼速度更快,占用空間更少。使用多維數(shù) ?
? 組時差異更明顯。下面的代碼作用是相同的,但是效率不一樣。 ?
? ?
? char* ? pBuffer ? = ? new ? char[100]; ?
? nTestNumber ? = ? 0; ?
? ?
? for(dwIndex ? = ? 0;dwIndex ? < ? 100;dwIndex ? ++) ?
? { ?
? nTestNumber ? = ? pBuffer[dwIndex]; ?
? } ?
? ?
? char* ? pTemp ? = ? pBuffer; ?
? for(dwIndex ? = ? 0;dwIndex ? < ? 100;dwIndex ? ++) ?
? { ?
? nTestNumber ? = ? *(pTemp ? ++); ?
? } ?
? ?
? 指針方法的優(yōu)點是,pBuffer的地址每次裝入地址pTemp后,在每次循環(huán)中只需對pTemp增量操作。 ?
? 在數(shù)組索引方法中,每次循環(huán)中都必須進行基于dwIndex值求數(shù)組下標的復雜運算。 ?
? */??