内联函数的声明和定义
一、什么叫inline函數(shù)?
inline(小心,不是online),翻譯成“內(nèi)聯(lián)”或“內(nèi)嵌”。意指:當(dāng)編譯器發(fā)現(xiàn)某段代碼在調(diào)用一個(gè)內(nèi)聯(lián)函數(shù)時(shí),它不是去調(diào)用該函數(shù),而是將該函數(shù)的代碼,整段插入到當(dāng)前位置。這樣做的好處是省去了調(diào)用的過(guò)程,加快程序運(yùn)行速度。(函數(shù)的調(diào)用過(guò)程,由于有前面所說(shuō)的參數(shù)入棧等操作,所以總要多占用一些時(shí)間)。這樣做的不好處:由于每當(dāng)代碼調(diào)用到內(nèi)聯(lián)函數(shù),就需要在調(diào)用處直接插入一段該函數(shù)的代碼,所以程序的體積將增大。拿生活現(xiàn)象比喻,就像電視壞了,通過(guò)電話找修理工來(lái),你會(huì)嫌慢,于是干脆在家里養(yǎng)了一個(gè)修理工。這樣當(dāng)然是快了,不過(guò),修理工住在你家可就要占地兒了。內(nèi)聯(lián)函數(shù)并不是必須的,它只是為了提高速度而進(jìn)行的一種修飾。要修飾一個(gè)函數(shù)為內(nèi)聯(lián)型,使用如下格式:
inline 函數(shù)的聲明或定義
簡(jiǎn)單一句話,在函數(shù)聲明或定義前加一個(gè) inline 修飾符。
內(nèi)聯(lián)函數(shù)的本質(zhì)是,節(jié)省時(shí)間但是消耗空間。
二、inline函數(shù)的規(guī)則
(1)一個(gè)函數(shù)可以自已調(diào)用自已,稱為遞歸調(diào)用(后面講到),含有遞歸調(diào)用的函數(shù)不能設(shè)置為inline;
(2)使用了復(fù)雜流程控制語(yǔ)句:循環(huán)語(yǔ)句和switch語(yǔ)句,無(wú)法設(shè)置為inline;
(3)由于inline增加體積的特性,所以建議inline函數(shù)內(nèi)的代碼應(yīng)很短小。最好不超過(guò)5行。
(4)inline僅做為一種“請(qǐng)求”,特定的情況下,編譯器將不理會(huì)inline關(guān)鍵字,而強(qiáng)制讓函數(shù)成為普通函數(shù)。出現(xiàn)這種情況,編譯器會(huì)給出警告消息。
(5)在你調(diào)用一個(gè)內(nèi)聯(lián)函數(shù)之前,這個(gè)函數(shù)一定要在之前有聲明或已定義為inline,如果在前面聲明為普通函數(shù),而在調(diào)用代碼后面才定義為一個(gè)inline函數(shù),程序可以通過(guò)編譯,但該函數(shù)沒(méi)有實(shí)現(xiàn)inline。比如下面代碼片段:
//函數(shù)一開始沒(méi)有被聲明為inline: void foo(); //然后就有代碼調(diào)用它: foo(); //在調(diào)用后才有定義函數(shù)為inline: inline void foo() { ...... }
代碼是的foo()函數(shù)最終沒(méi)有實(shí)現(xiàn)inline;
(6)、為了調(diào)試方便,在程序處于調(diào)試階段時(shí),所有內(nèi)聯(lián)函數(shù)都不被實(shí)現(xiàn)。
三、使用內(nèi)聯(lián)函數(shù)時(shí)應(yīng)注意以下幾個(gè)問(wèn)題:
(1) 在一個(gè)文件中定義的內(nèi)聯(lián)函數(shù)不能在另一個(gè)文件中使用。它們通常放在頭文件中共享。
(2) 內(nèi)聯(lián)函數(shù)應(yīng)該簡(jiǎn)潔,只有幾個(gè)語(yǔ)句,如果語(yǔ)句較多,不適合于定義為內(nèi)聯(lián)函數(shù)。
(3) 內(nèi)聯(lián)函數(shù)體中,不能有循環(huán)語(yǔ)句、if語(yǔ)句或switch語(yǔ)句,否則,函數(shù)定義時(shí)即使有inline關(guān)鍵字,編譯器也會(huì)把該函數(shù)作為非內(nèi)聯(lián)函數(shù)處理。
(4) 內(nèi)聯(lián)函數(shù)要在函數(shù)被調(diào)用之前聲明。關(guān)鍵字inline 必須與函數(shù)定義體放在一起才能使函數(shù)成為內(nèi)聯(lián),僅將inline 放在函數(shù)聲明前面不起任何作用。
定義在類聲明之中的成員函數(shù)將自動(dòng)地成為內(nèi)聯(lián)函數(shù)
例如
class A{public:void Foo(int x, int y) { } // 自動(dòng)地成為內(nèi)聯(lián)函數(shù)}將成員函數(shù)的定義體放在類聲明之中雖然能帶來(lái)書寫上的方便,但不是一種良好的編程風(fēng)格,上例應(yīng)該改成:
// 頭文件class A{public:void Foo(int x, int y);} // 定義文件inline void A::Foo(int x, int y){}慎用內(nèi)聯(lián)
內(nèi)聯(lián)能提高函數(shù)的執(zhí)行效率,為什么不把所有的函數(shù)都定義成內(nèi)聯(lián)函數(shù)?如果所有的函數(shù)都是內(nèi)聯(lián)函數(shù),還用得著“內(nèi)聯(lián)”這個(gè)關(guān)鍵字嗎?內(nèi)聯(lián)是以代碼膨脹(復(fù)制)為代價(jià),僅僅省去了函數(shù)調(diào)用的開銷,從而提高函數(shù)的執(zhí)行效率。如果執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間,相比于函數(shù)調(diào)用的開銷較大,那么效率的收獲會(huì)很少。另一方面,每一處內(nèi)聯(lián)函數(shù)的調(diào)用都要復(fù)制代碼,將使程序的總代碼量增大,消耗更多的內(nèi)存空間。
以下情況不宜使用內(nèi)聯(lián):
(1)如果函數(shù)體內(nèi)的代碼比較長(zhǎng),使用內(nèi)聯(lián)將導(dǎo)致內(nèi)存消耗代價(jià)較高。
(2)如果函數(shù)體內(nèi)出現(xiàn)循環(huán),那么執(zhí)行函數(shù)體內(nèi)代碼的時(shí)間要比函數(shù)調(diào)用的開銷大。類的構(gòu)造函數(shù)和析構(gòu)函數(shù)容易讓人誤解成使用內(nèi)聯(lián)更有效。要當(dāng)心構(gòu)造函數(shù)和析構(gòu)函數(shù)可能會(huì)隱藏一些行為,如“偷偷地”執(zhí)行了基類或成員對(duì)象的構(gòu)造函數(shù)和析構(gòu)函數(shù)。所以不要隨便地將構(gòu)造函數(shù)和析構(gòu)函數(shù)的定義體放在類聲明中。一個(gè)好的編譯器將會(huì)根據(jù)函數(shù)的定義體,自動(dòng)地取消不值得的內(nèi)聯(lián)(這進(jìn)一步說(shuō)明了 inline 不應(yīng)該出現(xiàn)在函數(shù)的聲明中)。
注意點(diǎn):
內(nèi)聯(lián)函數(shù)既能夠去除函數(shù)調(diào)用所帶來(lái)的效率負(fù)擔(dān)又能夠保留一般函數(shù)的優(yōu)點(diǎn)。然而,內(nèi)聯(lián)函數(shù)并不是萬(wàn)能藥,在一些情況下,它甚至能夠降低程序的性能。因此在使用的時(shí)候應(yīng)該慎重。
1.我們先來(lái)看看內(nèi)聯(lián)函數(shù)給我們帶來(lái)的好處:從一個(gè)用戶的角度來(lái)看,內(nèi)聯(lián)函數(shù)看起來(lái)和普通函數(shù)一樣, 它可以有參數(shù)和返回值,也可以有自己的作用域,然而它卻不會(huì)引入一般函數(shù)調(diào)用所帶來(lái)的負(fù)擔(dān)。另外, 它可以比宏更安全更容易調(diào)試。
當(dāng)然有一點(diǎn)應(yīng)該意識(shí)到,inline specifier僅僅是對(duì)編譯器的建議,編譯器有權(quán)利忽略這個(gè)建議。那么編譯器是如何決定函數(shù)內(nèi)聯(lián)與否呢?一般情況下關(guān)鍵性因素包括函數(shù)體的大小,是否有局部對(duì)象被聲明,函數(shù)的復(fù)雜性等等。
2.那么如果一個(gè)函數(shù)被聲明為inline但是卻沒(méi)有被內(nèi)聯(lián)將會(huì)發(fā)生什么呢?理論上,當(dāng)編譯器拒絕內(nèi)聯(lián)一個(gè) 函數(shù)的時(shí)候,那個(gè)函數(shù)會(huì)像普通函數(shù)一樣被對(duì)待,但是還會(huì)出現(xiàn)一些其他的問(wèn)題。例如下面這段代碼:
因?yàn)槌蓡T函數(shù)Time::Show()包括一個(gè)局部變量和一個(gè)for循環(huán),所以編譯器一般拒絕inline,并且把它當(dāng)作一個(gè)普通的成員函數(shù)。但是這個(gè)包含類聲明的頭文件會(huì)被單獨(dú)的#include進(jìn)各個(gè)獨(dú)立的編譯單元中:
結(jié)果編譯器為這個(gè)程序生成了兩個(gè)相同成員函數(shù)的拷貝:
void f1();? void f2();? int main()? {? f1();? f2();? return 0;? }?
當(dāng)程序被鏈接的時(shí)候,linker將會(huì)面對(duì)兩個(gè)相同的Time::Show()拷貝,于是函數(shù)重定義的連接錯(cuò)誤發(fā)生。但是老一些的C++實(shí)現(xiàn)對(duì)付這種情況的辦法是通過(guò)把一個(gè)un-inlined函數(shù)當(dāng)作static來(lái)處理。因此每一份函數(shù)拷貝僅僅在自己的編譯單元中可見,這樣鏈接錯(cuò)誤就解決了,但是在程序中卻會(huì)留下多份函數(shù)拷貝。在這種情況下,程序的性能不但沒(méi)有提升,反而增加了編譯和鏈接時(shí)間以及最終可執(zhí)行體的大小。但是幸運(yùn)的是,新的C++標(biāo)準(zhǔn)中關(guān)于un-inlined函數(shù)的說(shuō)法已經(jīng)改變。一個(gè)符合標(biāo)準(zhǔn)C++實(shí)現(xiàn)應(yīng)該只生成一份函數(shù)拷貝。然而,要想所有的編譯器都支持這一點(diǎn)可能還需要很長(zhǎng)時(shí)間。
另外關(guān)于內(nèi)聯(lián)函數(shù)還有兩個(gè)更令人頭疼的問(wèn)題。第一個(gè)問(wèn)題是該如何進(jìn)行維護(hù)。一個(gè)函數(shù)開始的時(shí)候可能以內(nèi)聯(lián)的形式出現(xiàn),但是隨著系統(tǒng)的擴(kuò)展,函數(shù)體可能要求添加額外的功能,結(jié)果內(nèi)聯(lián)函數(shù)就變得不太可能,因此需要把inline specifier去除以及把函數(shù)體放到一個(gè)單獨(dú)的源文件中。另一個(gè)問(wèn)題是當(dāng)內(nèi)聯(lián)函數(shù)被應(yīng)用在代碼庫(kù)的時(shí)候產(chǎn)生。當(dāng)內(nèi)聯(lián)函數(shù)改變的時(shí)候,用戶必須重新編譯他們的代碼以反映這種改變。然而對(duì)于一個(gè)非內(nèi)聯(lián)函數(shù),用戶僅僅需要重新鏈接就可以了。
這里想要說(shuō)的是,內(nèi)聯(lián)函數(shù)并不是一個(gè)增強(qiáng)性能的靈丹妙藥。只有當(dāng)函數(shù)非常短小的時(shí)候它才能得到我們想要的效果,但是如果函數(shù)并不是很短而且在很多地方都被調(diào)用的話,那么將會(huì)使得可執(zhí)行體的體積增大。最令人煩惱的還是當(dāng)編譯器拒絕內(nèi)聯(lián)的時(shí)候。在老的實(shí)現(xiàn)中,結(jié)果很不盡人意,雖然在新的實(shí)現(xiàn)中有很大的改善,但是仍然還是不那么完善的。一些編譯器能夠足夠的聰明來(lái)指出哪些函數(shù)可以內(nèi)聯(lián)哪些不能,但是,大多數(shù)編譯器就不那么聰明了,因此這就需要我們的經(jīng)驗(yàn)來(lái)判斷。如果內(nèi)聯(lián)函數(shù)不能增強(qiáng)行能,就避免使用它!
?
總結(jié)
以上是生活随笔為你收集整理的内联函数的声明和定义的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: c++ primer 5th,习题13.
- 下一篇: xhtml使用style属性