#C++基礎# inline __forceinline __attribute__((always_inline)內聯函數

inline 

inline是C++關鍵字,在函數聲明或定義中,函數返回類型前加上關鍵字inline,即可以把函數指定爲內聯函數。這樣可以解決一些頻繁調用的函數大量消耗棧空間(棧內存)的問題。關鍵字inline必須與函數定義放在一起才能使函數成爲內聯函數,僅僅將inline放在函數聲明前面不起任何作用。inline是一種“用於實現”的關鍵字,而不是一種“用於聲明”的關鍵字。

錯誤全局函數例子:function_inline

inline void function_inline();
void function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}

正確全局函數例子:function_inline

inline void function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}

錯誤成員函數例子:class_inline::function_inline

//class_inline.h
class class_inline 
{
    public: 
    inline void function_inline();
};

//class_inline.cpp
void class_inline::function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}

正確成員函數例子:class_inline::function_inline

推薦!

//class_inline.h
class class_inline 
{
public: 
    inline void function_inline() 
    {
        std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
    }
};

不推薦! 

//class_inline.h
class class_inline 
{
public: 
    inline void function_inline();
};

//class_inline.cpp
inline void class_inline::function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}

不推薦!  

//class_inline.h
class class_inline {
public: 
    void function_inline();
};

//class_inline.cpp
inline void class_inline::function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << std::endl;
}

inline函數僅僅是一個對編譯器的建議,所以最後能否真正內聯,看編譯器的意思,它如果認爲函數不復雜,能在調用點展開,就會真正內聯,並不是說聲明瞭內聯就會內聯,聲明內聯只是一個建議而已。

Also, this, for GCC/C99.

The extent to which suggestions made by using the inline function specifier are effective (C99 6.7.4).

  • GCC will not inline any functions if the -fno-inline option is used or if -O0 is used. Otherwise, GCC may still be unable to inline a function for many reasons; the -Winline option may be used to determine if a function has not been inlined and why not.

 

運的是大多數編譯器提供了一個診斷級別:如果它們無法將你要求的函數 inline 化, 會給你一個警告信息。

 

inline限制

inline只適合函數體內簡單代碼的涵數使用。不能包含複雜的結構控制語句例如while、switch,並且不能內聯函數本身不能是直接遞歸函數(即,自己內部還調用自己的函數)。而所有(除了最平凡,幾乎什麼也沒做)的虛擬函數,都會阻止inline的進行。因爲virtual意味着”等待,直到執行時期再確定應該調用哪一個函數“,而inline卻意味着”在編譯階段,將調用動作以被調用函數的主體取代之“。如果編譯器做決定時,尚不知道該調用哪一個函數,很難責成他們做出一個inline函數。

 

慎用inline

內聯爲提高函數的執行效率,以代碼膨脹(複製)爲代價,僅僅省去了函數調用的開銷,從而提高函數的執行效率。 如果執行函數體內代碼的時間,相比於函數調用的開銷較大,那麼效率的收穫會很少。另一方面,每一處內聯函數的調用都要複製代碼,將使程序的總代碼量增大,消耗更多的內存空間。因此,需要酌情使用。

以下情況不宜使用內聯: 

  • (1) 如果函數體內的代碼比較長,使用內聯將導致內存消耗代價較高。 
  • (2) 如果函數體內出現循環,那麼執行函數體內代碼的時間要比函數調用的開銷大。
  • (3)  類的構造函數和析構函數容易讓人誤解成使用內聯更有效。要當心構造函數和析構函數可能會隱藏一些行爲,如“偷偷地”執行了基類或成員對象的構造函數和析構函數。所以不要隨便地將構造函數和析構函數的定義體放在類聲明中。

一個好的編譯器將會根據函數的定義體,自動地取消不值得的內聯(這進一步說明了 inline 不應該出現在函數的聲明中)。即“inline函數僅僅是一個對編譯器的建議,所以最後能否真正內聯,看編譯器的意思”,當有編譯宏明確需要使用內聯時,請使用__forceinline __attribute__((always_inline)

 

__forceinline

編譯器嘗試內聯函數,而不考慮函數的特徵,在某些情況下,編譯器可能會選擇忽略__forceinline,而不內聯函數。例如:

  •     遞歸函數永遠不會內聯到自身中。
  •     使用alloca()的函數從不內聯。

當有編譯宏(COMPLIE_ZMODULE_NAME)替換時,最好還是強制內聯以確保debug版本(不會編譯內聯函數,即使加了inline關鍵字)與release版本(可能優化爲內聯函數),從而導致COMPLIE_ZMODULE_NAME的內容存在差異引起一系列問題。

__forceinline void function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << "COMPLIE_ZMODULE_NAME:" << COMPLIE_ZMODULE_NAME << std::endl;
}

__attribute__((always_inline)

編譯器嘗試內聯函數,而不考慮函數的特徵,在某些情況下,編譯器可能會選擇忽略__attribute__((always_inline)函數屬性,而不內聯函數。例如:

  •     遞歸函數永遠不會內聯到自身中。
  •     使用alloca()的函數從不內聯。

當有編譯宏(COMPLIE_ZMODULE_NAME)替換時,最好還是強制內聯以確保debug版本(不會編譯內聯函數,即使加了inline關鍵字)與release版本(可能優化爲內聯函數),從而導致COMPLIE_ZMODULE_NAME的內容存在差異引起一系列問題。

inline void __attribute__((always_inline)) function_inline()
{
    std::cout << __FILE__ << ":" << __LINE__ << "/" << __FUNCTION__ << "COMPLIE_ZMODULE_NAME:" << COMPLIE_ZMODULE_NAME << std::endl;
}

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章