內(nèi)聯(lián)
背景:
函數(shù)的作用:使用函數(shù)能夠避免將相同代碼重寫(xiě)多次的麻煩舵揭,還能減少可執(zhí)行程序的體積,但也會(huì)帶來(lái)程序運(yùn)行時(shí)間上的開(kāi)銷(xiāo)躁锡。
函數(shù)執(zhí)行過(guò)程:函數(shù)調(diào)用在執(zhí)行時(shí)午绳,首先要在棧中為形參和局部變量分配存儲(chǔ)空間,然后還要將實(shí)參的值復(fù)制給形參映之,接下來(lái)還要將函數(shù)的返回地址(該地址指明了函數(shù)執(zhí)行結(jié)束后拦焚,程序應(yīng)該回到哪里繼續(xù)執(zhí)行)放入棧中蜡坊,最后才跳轉(zhuǎn)到函數(shù)內(nèi)部執(zhí)行。這個(gè)過(guò)程是要耗費(fèi)時(shí)間的赎败。
函數(shù)返回后:函數(shù)執(zhí)行 return 語(yǔ)句返回時(shí)秕衙,需要從棧中回收形參和局部變量占用的存儲(chǔ)空間,然后從棧中取出返回地址僵刮,再跳轉(zhuǎn)到該地址繼續(xù)執(zhí)行据忘,這個(gè)過(guò)程也要耗費(fèi)時(shí)間。
總結(jié):使用函數(shù)調(diào)用語(yǔ)句和直接把函數(shù)中的代碼重新抄寫(xiě)一遍相比搞糕,節(jié)省了人力勇吊,但是帶來(lái)了程序運(yùn)行時(shí)間上的額外開(kāi)銷(xiāo)。
一般情況下窍仰,這個(gè)開(kāi)銷(xiāo)可以忽略不計(jì)汉规。但是,如果一個(gè)函數(shù)內(nèi)部沒(méi)有幾條語(yǔ)句驹吮,執(zhí)行時(shí)間本來(lái)就非常短针史,那么這個(gè)函數(shù)調(diào)用產(chǎn)生的額外開(kāi)銷(xiāo)和函數(shù)本身執(zhí)行的時(shí)間相比,就顯得不能忽略了碟狞。假如這樣的函數(shù)在一個(gè)循環(huán)中被上千萬(wàn)次地執(zhí)行啄枕,函數(shù)調(diào)用導(dǎo)致的時(shí)間開(kāi)銷(xiāo)可能就會(huì)使得程序運(yùn)行明顯變慢。
用 inline 關(guān)鍵字較好地解決了函數(shù)調(diào)用開(kāi)銷(xiāo)的問(wèn)題族沃。
內(nèi)聯(lián)函數(shù)和普通函數(shù)的區(qū)別在于:當(dāng)編譯器處理調(diào)用內(nèi)聯(lián)函數(shù)的語(yǔ)句時(shí)频祝,不會(huì)將該語(yǔ)句編譯成函數(shù)調(diào)用的指令,而是直接將整個(gè)函數(shù)體的代碼插人調(diào)用語(yǔ)句處竭业,就像整個(gè)函數(shù)體在調(diào)用處被重寫(xiě)了一遍一樣智润。
有了內(nèi)聯(lián)函數(shù)及舍,就能像調(diào)用一個(gè)函數(shù)那樣方便地重復(fù)使用一段代碼未辆,而不需要付出執(zhí)行函數(shù)調(diào)用的額外開(kāi)銷(xiāo)。很顯然锯玛,使用內(nèi)聯(lián)函數(shù)會(huì)使最終可執(zhí)行程序的體積增加咐柜。
內(nèi)聯(lián)函數(shù)中的代碼應(yīng)該只是很簡(jiǎn)單、執(zhí)行很快的幾條語(yǔ)句攘残。如果一個(gè)函數(shù)較為復(fù)雜拙友,它執(zhí)行的時(shí)間可能上萬(wàn)倍于函數(shù)調(diào)用的額外開(kāi)銷(xiāo),那么將其作為內(nèi)聯(lián)函數(shù)處理的結(jié)果是付出讓代碼體積增加不少的代價(jià)歼郭,卻只使速度提高了萬(wàn)分之一遗契,這顯然是不劃算的。
有時(shí)函數(shù)看上去很簡(jiǎn)單病曾,例如只有一個(gè)包含一兩條語(yǔ)句的循環(huán)牍蜂,但該循環(huán)的執(zhí)行次數(shù)可能很多漾根,要消耗大量時(shí)間,那么這種情況也不適合將其實(shí)現(xiàn)為內(nèi)聯(lián)函數(shù)鲫竞。
另外辐怕,需要注意的是,inline要放在函數(shù)定義前从绘,而不是函數(shù)聲明前寄疏。
內(nèi)聯(lián)函數(shù)的使用規(guī)則:
只有當(dāng)函數(shù)只有 10 行甚至更少時(shí)才將其定義為內(nèi)聯(lián)函數(shù)
精短的存取函數(shù)聲明稱(chēng)內(nèi)聯(lián)
謹(jǐn)慎對(duì)待析構(gòu)函數(shù), 析構(gòu)函數(shù)往往比其表面看起來(lái)要更長(zhǎng), 因?yàn)橛须[含的成員和基類(lèi)析構(gòu)函數(shù)被調(diào)用
包含循環(huán)或 switch 語(yǔ)句的函數(shù)常常是得不償失 (除非在大多數(shù)情況下, 這些循環(huán)或 switch 語(yǔ)句從不被執(zhí)行)
虛函數(shù)和遞歸函數(shù)不會(huì)被內(nèi)聯(lián)
特定場(chǎng)景規(guī)則:
- 完全在類(lèi)/結(jié)構(gòu)/聯(lián)合定義中定義的函數(shù),無(wú)論是成員函數(shù)還是友元函數(shù)僵井,如果它添加到全局模塊 (C++20 起)陕截,則為隱式的內(nèi)聯(lián)函數(shù)。
- 聲明為
constexpr
的函數(shù)是一個(gè)隱式的內(nèi)聯(lián)函數(shù)(C++11) - 聲明為
deleted
的函數(shù)是一個(gè)隱式的內(nèi)聯(lián)函數(shù)(C++11)(暫未明確:deleted的函數(shù)內(nèi)聯(lián)個(gè)什么勁) - 靜態(tài)全局區(qū)的變量驹沿,如:靜態(tài)類(lèi)成員或者命名空間范圍的變量艘策,可以聲明成內(nèi)聯(lián)變量
- 聲明成
constexpr
的靜態(tài)類(lèi)成員變量(命名空間范圍變量除外)是一個(gè)隱性的內(nèi)聯(lián)變量
優(yōu)點(diǎn): 當(dāng)函數(shù)體比較小的時(shí)候, 內(nèi)聯(lián)該函數(shù)可以令目標(biāo)代碼更加高效. 對(duì)于存取函數(shù)以及其它函數(shù)體比較短, 性能關(guān)鍵的函數(shù), 鼓勵(lì)使用內(nèi)聯(lián).
缺點(diǎn): 濫用內(nèi)聯(lián)將導(dǎo)致程序變慢. 內(nèi)聯(lián)可能使目標(biāo)代碼量或增或減, 這取決于內(nèi)聯(lián)函數(shù)的大小. 內(nèi)聯(lián)非常短小的存取函數(shù)通常會(huì)減少代碼大小, 但內(nèi)聯(lián)一個(gè)相當(dāng)大的函數(shù)將戲劇性的增加代碼大小. 現(xiàn)代處理器由于更好的利用了指令緩存, 小巧的代碼往往執(zhí)行更快。
注意:遞歸函數(shù)不應(yīng)該聲明成內(nèi)聯(lián)函數(shù).(遞歸調(diào)用堆棧的展開(kāi)并不像循環(huán)那么簡(jiǎn)單, 比如遞歸層數(shù)在編譯時(shí)可能是未知的, 大多數(shù)編譯器都不支持內(nèi)聯(lián)遞歸函數(shù)). 虛函數(shù)內(nèi)聯(lián)的主要原因則是想把它的函數(shù)體放在類(lèi)定義內(nèi)
notes:
查看匯編代碼是否內(nèi)聯(lián)時(shí)渊季,應(yīng)該用release版本朋蔫,debug版本看不到內(nèi)聯(lián)的情況
在線編譯器,可驗(yàn)證不同的編譯器下的編譯選項(xiàng)或差異性
參考資料:
cppreference:inline
被知乎大佬嘲諷后的一個(gè)月却汉,我重新研究了一下內(nèi)聯(lián)函數(shù)
C++ 風(fēng)格指南