inline用法詳解
(一)inline函式(摘自C++ Primer的第三版)
在函式宣告或定義中函式返回型別前加上關鍵字inline即把min()指定為內聯。
inline int min(int first, int secend) {/****/};
inline函式對編譯器而言必須是可見的,以便它能夠在呼叫點內展開該函式。與非inline函式不同的是,inline函式必須在呼叫該函式的每個文字檔案中定義。當然,對於同一程式的不同檔案,如果inline函式出現的話,其定義必須相同。對於由兩個檔案compute.C和draw.C構成的程式來說,程式設計師不能定義這樣的min()函式,它在compute.C中指一件事情,而在draw.C中指另外一件事情。如果兩個定義不相同,程式將會有未定義的行為.
為保證不會發生這樣的事情,建議把inline函式的定義放到標頭檔案中。在每個呼叫該inline函式的檔案中包含該標頭檔案。這種方法保證對每個inline函式只有一個定義,且程式設計師無需複製程式碼,並且不可能在程式的生命期中引起無意的不匹配的事情。
(二)行內函數的程式設計風格(摘自高質量C++/C 程式設計指南)
關鍵字inline 必須與函式定義體放在一起才能使函式成為內聯,僅將inline 放在函式宣告前面不起任何作用。
如下風格的函式Foo 不能成為行內函數:
inline void Foo(int x, int y); // inline 僅與函式宣告放在一起
void Foo(int x, int y)
{
}
而如下風格的函式Foo 則成為行內函數:
void Foo(int x, int y);
inline void Foo(int x, int y) // inline 與函式定義體放在一起
{
}
所以說,inline 是一種“用於實現的關鍵字”,而不是一種“用於宣告的關鍵字”。一般地,使用者可以閱讀函式的宣告,但是看不到函式的定義。儘管在大多數教科書中行內函數的宣告、定義體前面都加了inline 關鍵字,但我認為inline 不應該出現在函式的宣告中。這個細節雖然不會影響函式的功能,但是體現了高質量C++/C 程式設計風格的一個基本原則:宣告與定義不可混為一談,使用者沒有必要、也不應該知道函式是否需要內聯。
定義在類宣告之中的成員函式將自動地成為行內函數,例如
class A
{
public:
void Foo(int x, int y) { } // 自動地成為行內函數
}
將成員函式的定義體放在類宣告之中雖然能帶來書寫上的方便,但不是一種良好的程式設計
風格,上例應該改成:
// 標頭檔案
class A
{
public:
void Foo(int x, int y);
}
// 定義檔案
inline void A::Foo(int x, int y)
{
}
慎用內聯
內聯能提高函式的執行效率,為什麼不把所有的函式都定義成行內函數?
如果所有的函式都是行內函數,還用得著“內聯”這個關鍵字嗎?
內聯是以程式碼膨脹(複製)為代價,僅僅省去了函式呼叫的開銷,從而提高函式的
執行效率。如果執行函式體內程式碼的時間,相比於函式呼叫的開銷較大,那麼效率的收
獲會很少。另一方面,每一處行內函數的呼叫都要複製程式碼,將使程式的總程式碼量增大,
消耗更多的記憶體空間。以下情況不宜使用內聯:
(1)如果函式體內的程式碼比較長,使用內聯將導致記憶體消耗代價較高。
(2)如果函式體內出現迴圈,那麼執行函式體內程式碼的時間要比函式呼叫的開銷大。
類的建構函式和解構函式容易讓人誤解成使用內聯更有效。要當心建構函式和析構
函式可能會隱藏一些行為,如“偷偷地”執行了基類或成員物件的建構函式和解構函式。
所以不要隨便地將建構函式和解構函式的定義體放在類宣告中。
一個好的編譯器將會根據函式的定義體,自動地取消不值得的內聯(這進一步說明
了inline 不應該出現在函式的宣告中)。
C++ 語言支援函式內聯,其目的是為了提高函式的執行效率(速度)。
在C程式中,可以用巨集程式碼提高執行效率。巨集程式碼本身不是函式,但使用起來象函式。
前處理器用複製巨集程式碼的方式代替函式呼叫,省去了引數壓棧、生成組合語言的CALL呼叫、
返回引數、執行return等過程,從而提高了速度。
使用巨集程式碼最大的缺點是容易出錯,前處理器在複製巨集程式碼時常常產生意想不到的邊際效應。
對於C++ 而言,使用巨集程式碼還有另一種缺點:無法操作類的私有資料成員。
讓我們看看C++ 的"函式內聯"是如何工作的。
對於任何行內函數,編譯器在符號表裡放入函式的宣告(包括名字、引數型別、返回值型別)。
如果編譯器沒有發現行內函數存在錯誤,那麼該函式的程式碼也被放入符號表裡。
在呼叫一個行內函數時,編譯器首先檢查呼叫是否正確
(進行型別安全檢查,或者進行自動型別轉換,當然對所有的函式都一樣)。
如果正確,行內函數的程式碼就會直接替換函式呼叫,於是省去了函式呼叫的開銷。
這個過程與預處理有顯著的不同,因為前處理器不能進行型別安全檢查,或者進行自動型別轉換。
假如行內函數是成員函式,物件的地址(this)會被放在合適的地方,這也是前處理器辦不到的。
C++ 語言的函式內聯機制既具備巨集程式碼的效率,又增加了安全性,而且可以自由操作類的資料成員。
所以在C++ 程式中,應該用行內函數取代所有巨集程式碼,"斷言assert"恐怕是唯一的例外。
assert是僅在Debug版本起作用的巨集,它用於檢查"不應該"發生的情況。
為了不在程式的Debug版本和Release版本引起差別,assert不應該產生任何副作用。
如果assert是函式,由於函式呼叫會引起記憶體、程式碼的變動,那麼將導致Debug版本與Release版本存在差異。
所以assert不是函式,而是巨集。
相關文章
- extern用法詳解
- Metasploit用法詳解
- xargs用法詳解
- Nmap用法詳解
- mount用法詳解
- C++ 中 inline 用法概述C++inline
- inline內聯的用法與作用inline
- Flutter ListView 用法詳解FlutterView
- MyBatis Generator 用法詳解MyBatis
- iconfont用法詳解
- Promise用法詳解(一)Promise
- StringTie用法詳解
- SVG <markers>用法詳解SVG
- Elasticsearch SQL用法詳解ElasticsearchSQL
- git stash用法詳解Git
- JSONP用法詳解JSON
- Generator用法詳解+co
- appendChild()用法詳解APP
- jQuery 事件用法詳解jQuery事件
- SVG transform用法詳解SVGORM
- expdp/impdp 用法詳解
- expdp/impdp用法詳解
- awk sed 用法詳解
- Ubuntu mount命令用法詳解Ubuntu
- axios的用法詳解iOS
- react-dnd 用法詳解React
- golang package time 用法詳解GolangPackage
- c++ vector用法詳解C++
- dataTransfer.setData() 用法詳解
- struct的匿名用法詳解Struct
- Python self用法詳解Python
- fcntl函式用法詳解函式
- eval()函式用法詳解函式
- jQuery stop()方法用法詳解jQuery
- STL中set用法詳解
- background屬性用法詳解
- document.all用法詳解
- rowspan和colspan用法詳解