C++ 關(guān)鍵字 inline詳細(xì)介紹
1. 內(nèi)聯(lián)函數(shù)
在C++中我們通常定義以下函數(shù)來求兩個(gè)整數(shù)的最大值:
int max(int a, int b)
{
return a > b ? a : b;
}
為這么一個(gè)小的操作定義一個(gè)函數(shù)的好處有:
① 閱讀和理解函數(shù) max 的調(diào)用,要比讀一條等價(jià)的條件表達(dá)式并解釋它的含義要容易得多
② 如果需要做任何修改,修改函數(shù)要比找出并修改每一處等價(jià)表達(dá)式容易得多
③ 使用函數(shù)可以確保統(tǒng)一的行為,每個(gè)測(cè)試都保證以相同的方式實(shí)現(xiàn)
④ 函數(shù)可以重用,不必為其他應(yīng)用程序重寫代碼
雖然有這么多好處,但是寫成函數(shù)有一個(gè)潛在的缺點(diǎn):調(diào)用函數(shù)比求解等價(jià)表達(dá)式要慢得多。在大多數(shù)的機(jī)器上,調(diào)用函數(shù)都要做很多工作:調(diào)用前要先保存寄存器,并在返回時(shí)恢復(fù),復(fù)制實(shí)參,程序還必須轉(zhuǎn)向一個(gè)新位置執(zhí)行
C++中支持內(nèi)聯(lián)函數(shù),其目的是為了提高函數(shù)的執(zhí)行效率,用關(guān)鍵字 inline 放在函數(shù)定義(注意是定義而非聲明,下文繼續(xù)講到)的前面即可將函數(shù)指定為內(nèi)聯(lián)函數(shù),內(nèi)聯(lián)函數(shù)通常就是將它在程序中的每個(gè)調(diào)用點(diǎn)上“內(nèi)聯(lián)地”展開,假設(shè)我們將 max 定義為內(nèi)聯(lián)函數(shù):
inline int max(int a, int b)
{
return a > b ? a : b;
}
則調(diào)用: cout<<max(a, b)<<endl;
在編譯時(shí)展開為: cout<<(a > b ? a : b)<<endl;
從而消除了把 max寫成函數(shù)的額外執(zhí)行開銷
2. 內(nèi)聯(lián)函數(shù)和宏
無論是《Effective C++》中的 “Prefer consts,enums,and inlines to #defines” 條款,還是《高質(zhì)量程序設(shè)計(jì)指南——C++/C語言》中的“用函數(shù)內(nèi)聯(lián)取代宏”,宏在C++中基本是被廢了,在書《高質(zhì)量程序設(shè)計(jì)指南——C++/C語言》中這樣解釋到:
3. 將內(nèi)聯(lián)函數(shù)放入頭文件
關(guān)鍵字 inline 必須與函數(shù)定義體放在一起才能使函數(shù)成為內(nèi)聯(lián),僅將 inline 放在函數(shù)聲明前面不起任何作用。
如下風(fēng)格的函數(shù) Foo 不能成為內(nèi)聯(lián)函數(shù):
inline void Foo(int x, int y); // inline 僅與函數(shù)聲明放在一起
void Foo(int x, int y)
{
...
}
而如下風(fēng)格的函數(shù) Foo 則成為內(nèi)聯(lián)函數(shù):
void Foo(int x, int y);
inline void Foo(int x, int y) // inline 與函數(shù)定義體放在一起
{
...
}
所以說,C++ inline函數(shù)是一種“用于實(shí)現(xiàn)的關(guān)鍵字”,而不是一種“用于聲明的關(guān)鍵字”。一般地,用戶可以閱讀函數(shù)的聲明,但是看不到函數(shù)的定義。盡管在大多數(shù)教科書中內(nèi)聯(lián)函數(shù)的聲明、定義體前面都加了 inline 關(guān)鍵字,但我認(rèn)為 inline 不應(yīng)該出現(xiàn)在函數(shù)的聲明中。這個(gè)細(xì)節(jié)雖然不會(huì)影響函數(shù)的功能,但是體現(xiàn)了高質(zhì)量C++/C 程序設(shè)計(jì)風(fēng)格的一個(gè)基本原則:聲明與定義不可混為一談,用戶沒有必要、也不應(yīng)該知道函數(shù)是否需要內(nèi)聯(lián)。
定義在類聲明之中的成員函數(shù)將自動(dòng)地成為內(nèi)聯(lián)函數(shù),例如:
class A
{
public:
void Foo(int x, int y) { ... } // 自動(dòng)地成為內(nèi)聯(lián)函數(shù)
}
但是編譯器是否將它真正內(nèi)聯(lián)則要看 Foo函數(shù)如何定義
內(nèi)聯(lián)函數(shù)應(yīng)該在頭文件中定義,這一點(diǎn)不同于其他函數(shù)。編譯器在調(diào)用點(diǎn)內(nèi)聯(lián)展開函數(shù)的代碼時(shí),必須能夠找到 inline 函數(shù)的定義才能將調(diào)用函數(shù)替換為函數(shù)代碼,而對(duì)于在頭文件中僅有函數(shù)聲明是不夠的。
當(dāng)然內(nèi)聯(lián)函數(shù)定義也可以放在源文件中,但此時(shí)只有定義的那個(gè)源文件可以用它,而且必須為每個(gè)源文件拷貝一份定義(即每個(gè)源文件里的定義必須是完全相同的),當(dāng)然即使是放在頭文件中,也是對(duì)每個(gè)定義做一份拷貝,只不過是編譯器替你完成這種拷貝罷了。但相比于放在源文件中,放在頭文件中既能夠確保調(diào)用函數(shù)是定義是相同的,又能夠保證在調(diào)用點(diǎn)能夠找到函數(shù)定義從而完成內(nèi)聯(lián)(替換)。
但是你會(huì)很奇怪,重復(fù)定義那么多次,不會(huì)產(chǎn)生鏈接錯(cuò)誤?
我們來看一個(gè)例子:
A.h :
class A
{
public:
A(int a, int b) : a(a),b(b){}
int max();
private:
int a;
int b;
};
A.cpp :
#include "A.h"
inline int A::max()
{
return a > b ? a : b;
}
Main.cpp :
#include <iostream>
#include "A.h"
using namespace std;
inline int A::max()
{
return a > b ? a : b;
}
int main()
{
A a(3, 5);
cout<<a.max()<<endl;
return 0;
}
一切正常編譯,輸出結(jié)果:5
倘若你在Main.cpp中沒有定義max內(nèi)聯(lián)函數(shù),那么會(huì)出現(xiàn)鏈接錯(cuò)誤:
error LNK2001: unresolved external symbol "public: int __thiscall A::max(void)" (?max@A@@QAEHXZ)main.obj
找不到函數(shù)的定義,所以內(nèi)聯(lián)函數(shù)可以在程序中定義不止一次,只要 inline 函數(shù)的定義在某個(gè)源文件中只出現(xiàn)一次,而且在所有源文件中,其定義必須是完全相同的就可以。
在頭文件中加入或修改 inline 函數(shù)時(shí),使用了該頭文件的所有源文件都必須重新編譯。
4. 慎用內(nèi)聯(lián)
內(nèi)聯(lián)雖有它的好處,但是也要慎用,以下摘自《高質(zhì)量程序設(shè)計(jì)指南——C++/C語言》:
而在Google C++編碼規(guī)范中則規(guī)定得更加明確和詳細(xì):
內(nèi)聯(lián)函數(shù):
Tip: 只有當(dāng)函數(shù)只有 10 行甚至更少時(shí)才將其定義為內(nèi)聯(lián)函數(shù).
定義: 當(dāng)函數(shù)被聲明為內(nèi)聯(lián)函數(shù)之后, 編譯器會(huì)將其內(nèi)聯(lián)展開, 而不是按通常的函數(shù)調(diào)用機(jī)制進(jìn)行調(diào)用.
優(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í)行更快。
結(jié)論: 一個(gè)較為合理的經(jīng)驗(yàn)準(zhǔn)則是, 不要內(nèi)聯(lián)超過 10 行的函數(shù). 謹(jǐn)慎對(duì)待析構(gòu)函數(shù), 析構(gòu)函數(shù)往往比其表面看起來要更長(zhǎng), 因?yàn)橛须[含的成員和基類析構(gòu)函數(shù)被調(diào)用!
另一個(gè)實(shí)用的經(jīng)驗(yàn)準(zhǔn)則: 內(nèi)聯(lián)那些包含循環(huán)或 switch 語句的函數(shù)常常是得不償失 (除非在大多數(shù)情況下, 這些循環(huán)或 switch 語句從不被執(zhí)行).
有些函數(shù)即使聲明為內(nèi)聯(lián)的也不一定會(huì)被編譯器內(nèi)聯(lián), 這點(diǎn)很重要; 比如虛函數(shù)和遞歸函數(shù)就不會(huì)被正常內(nèi)聯(lián). 通常, 遞歸函數(shù)不應(yīng)該聲明成內(nèi)聯(lián)函數(shù).(遞歸調(diào)用堆棧的展開并不像循環(huán)那么簡(jiǎn)單, 比如遞歸層數(shù)在編譯時(shí)可能是未知的, 大多數(shù)編譯器都不支持內(nèi)聯(lián)遞歸函數(shù)). 虛函數(shù)內(nèi)聯(lián)的主要原因則是想把它的函數(shù)體放在類定義內(nèi), 為了圖個(gè)方便, 抑或是當(dāng)作文檔描述其行為, 比如精短的存取函數(shù).
-inl.h文件:
Tip: 復(fù)雜的內(nèi)聯(lián)函數(shù)的定義, 應(yīng)放在后綴名為 -inl.h 的頭文件中.
內(nèi)聯(lián)函數(shù)的定義必須放在頭文件中, 編譯器才能在調(diào)用點(diǎn)內(nèi)聯(lián)展開定義. 然而, 實(shí)現(xiàn)代碼理論上應(yīng)該放在 .cc 文件中, 我們不希望 .h 文件中有太多實(shí)現(xiàn)代碼, 除非在可讀性和性能上有明顯優(yōu)勢(shì).
如果內(nèi)聯(lián)函數(shù)的定義比較短小, 邏輯比較簡(jiǎn)單, 實(shí)現(xiàn)代碼放在 .h 文件里沒有任何問題. 比如, 存取函數(shù)的實(shí)現(xiàn)理所當(dāng)然都應(yīng)該放在類定義內(nèi). 出于編寫者和調(diào)用者的方便, 較復(fù)雜的內(nèi)聯(lián)函數(shù)也可以放到 .h 文件中, 如果你覺得這樣會(huì)使頭文件顯得笨重, 也可以把它萃取到單獨(dú)的 -inl.h 中. 這樣把實(shí)現(xiàn)和類定義分離開來, 當(dāng)需要時(shí)包含對(duì)應(yīng)的 -inl.h 即可。
本文參考書目:《C++ Primer》、《高質(zhì)量程序設(shè)計(jì)指南——C++/C語言》、Google C++編碼規(guī)范
相關(guān)文章
C語言中for循環(huán)問題(一個(gè)小坑需注意)
這篇文章主要給大家介紹了關(guān)于C語言中for循環(huán)問題的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2021-03-0364位linux 編譯c提示gnu/stubs-32.h:No such file or directory的解決方法
這篇文章主要介紹了64位linux 編譯c提示gnu/stubs-32.h:No such file or directory的解決方法,需要的朋友可以參考下2020-03-03C語言中隨機(jī)數(shù)rand()函數(shù)詳解
大家好,本篇文章主要講的是C語言中隨機(jī)數(shù)rand()函數(shù)詳解,感興趣的同學(xué)感快來看一看吧,對(duì)你有幫助的話記得收藏一下2022-02-02vector list map 遍歷刪除制定元素 防止迭代器失效的實(shí)例
下面小編就為大家?guī)硪黄獀ector list map 遍歷刪除制定元素 防止迭代器失效的實(shí)例。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-12-12C++模擬實(shí)現(xiàn)STL容器vector的示例代碼
這篇文章主要為大家詳細(xì)介紹了C++如何模擬實(shí)現(xiàn)STL容器vector的相關(guān)資料,文中的示例代碼講解詳細(xì),對(duì)我們學(xué)習(xí)C++有一定幫助,需要的可以參考一下2022-11-11