在ACE的原始碼目錄裡,有源檔案.cpp、標頭檔.h,我們還發現有以.i和.inl為副檔名的檔案。其實,以.i和.inl為副檔名的檔案是ACE源碼中inline函數的存放形式。
在說明ACE中為什麼採用這種方式來存放inline函數之前,我們來說一下inline關鍵字是什麼意識。我們知道當調用一個函數的時候,涉及到返回地址和參數壓棧等一些操作,這些操作是函數調用本身的開銷。
在原來的C代碼中,通常採用宏定義的方式類比函數,來消除函數調用的開銷,因此我們知道宏是在先行編譯時候進行處理的。但是,宏定義本身也有很多缺陷,很容易造成錯誤的使用。這就是inline關鍵字誕生的原因。
用inline關鍵字定義的函數,在編譯的時候,並不會產生真正的函數,而是在該函數調用處直接展開代碼,這樣就消除了函數調用的開銷。注意,inline關鍵字,只是給編譯器的一個暗示,是不是真的進行了inline處理,是由編譯器決定的。
那為什麼ACE會採用這樣一個特殊的方式來存放inline函呢?我們結合執行個體給出答案。
我們看一下,Reactor.h檔案的結尾處,有如下的處理:
#if defined (__ACE_INLINE__)
#include "ace/Reactor.i"
#endif /* __ACE_INLINE__ */
在看一下Reactor.cpp檔案開頭處的宏處理:
#if !defined (__ACE_INLINE__)
#include "ace/Reactor.i"
#endif /* __ACE_INLINE__ */
上面的Reactor.h,Reactor.cpp和Reactor.i檔案是ACE的Reactor架構的相關代碼。上面的宏定義我們很好理解,在標頭檔中的處理為:如果定義了宏__ACE_INLINE__,那麼我們就把Reactor.i檔案include到標頭檔中。在源檔案中處理為:如果沒有定義宏__ACE_INLINE__,那麼就把Reactor.i檔案include到源檔案中。其實有了上面inline含義的介紹,我們不難理解為什麼採用這種方式來進行處理。
這裡我們假設定義了宏__ACE_INLINE__,並且Reactor.i檔案是被include到源檔案裡,而不是被include標頭檔裡,那麼會產生什麼後果那?我們知道inline函數,在編譯器編譯後,是不會產生真正的函數的,因此,如果有其它源檔案,例如zhx.cpp,調用了Reactor.i檔案中的inline函數,那麼在串連的時候,就會拋出符號無法解析的錯誤,而如果Reactor.i檔案是被include到了標頭檔中,並且我們在zhx.cpp中有調用Reactor.i檔案中的函數,那麼在zhx.cpp中,只需要包含Reactor.h標頭檔即可,則Reactor.i的相關inline函數在zhx.cpp也進行了代碼展開處理。如果沒有定義宏__ACE_INLINE__,則Reactor.i被include到源檔案中,沒有任何問題,因為Reactor.i中的函數在編譯後,會產生真正的函數,而不是被inline處理。這就是ACE為什麼採用這樣的方式進行處理的原因。
__ACE_INLINE__開關就是用來設定是否將使用了該開關的inl中得函數是否以inline方式處理的。
在上面的介紹中,我們同時也發現了inline的缺點,就是它會造成代碼的膨脹。因此,不是什麼樣的函數都適合用inline來定義,只有那些短小的函數才適合採用inline處理。
註:ACE為什麼會採用.i和.inl兩種副檔名形式的檔案來存放inline函數,我還不是很清楚,但感覺以.inl形式存放的檔案是早期ACE代碼中的方式,後期的ACE代碼採用.i方式來存放inline函數,也就是說這應該是一個曆史遺留問題^_^,開源項目的缺點。