程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> Item 30:理解inline函數的裡裡外外 Effective C++筆記

Item 30:理解inline函數的裡裡外外 Effective C++筆記

編輯:C++入門知識

Item 30:理解inline函數的裡裡外外 Effective C++筆記


Item 30: Understanding the ins and outs of lining.

inline(內聯)函數的好處太多了:它沒有宏的那些缺點,見Item 2:避免使用define;而且不需要付出函數調用的代價。 同時也方便了編譯器基於上下文的優化。但inline函數也並非免費的午餐:

它會使得目標代碼膨脹,運行時會占用更多的內存,甚至引起緩存頁的失效和指令緩存的Miss,這些都會造成運行時性能的下降。 但是另一方面,如果inline函數足夠小以至於生成的目標代碼比函數調用還小,那麼inline函數會產生更小的目標代碼以及更高的指令緩存命中率。

本文便來討論一些典型的適合inline的場景,以及容易誤用inline的地方。

聲明方式

可能很多開發者不知道,inline只是對編譯器的一個請求而非命令。該請求可以隱式地進行也可以顯式地聲明。

當你的函數較復雜(比如有循環、遞歸),或者是虛函數時,編譯器很可能會拒絕把它inline。因為虛函數調用只有運行時才能決定調用哪個,而inline是在編譯器便要嵌入函數體。 有些編譯器在dianotics級別編譯時,會對拒絕inline給出warning。

隱式的辦法便是把函數定義放在類的定義中:

class Person{
    ...
    int age() const{ return _age;}  // 這會生成一個inline函數!
};

例子中是成員函數,如果是友元函數也是一樣的。除非友元函數定義在類的外面。

顯式的聲明則是使用inline限定符:

template
inline const T& max(const T& a, const T& b){ return a

模板與inline

可能你也注意到了inline函數和模板一般都定義在頭文件中。這是因為inline操作是在編譯時進行的,而模板的實例化也是編譯時進行的。 所以編譯器時便需要知道它們的定義。

在絕大多數C++環境中,inline都發生在編譯期。有些環境下也可以在鏈接時進行inline,尤其在.NET中可以運行時進行inline。

但模板實例化和inline是兩個過程,如果你的函數需要做成inline的就把它聲明為inline(也可以隱式地),否則仍然把它聲明為正常的函數。

取函數地址

有些適合inline的函數編譯器仍然不能把它inline,比如你要取一個函數的地址時:

inline void f(){}
void (*pf)() = f;

f();        // 這個調用將會被inline,它是個普通的函數調用
pf();       // 這個是通過指針調用的,不會被inline

構造/析構函數

構造析構函數看起來很適合inline,但事實並非如此。我們知道C++會在對象創建和銷毀時保證做很多事情,比如調用new時會導致構造函數被調用, 退出作用域時析構函數被調用,構造函數調用前成員對象的構造函數被調用,構造失敗後成員對象被析構等等。

這些事情不是平白無故發生的,編譯器會生成一些代碼並在編譯時插入你的程序。比如編譯後一個類的構造過程可能是這樣的:

Derived::Derived(){
    Base::Base();
    try{ data1.std::string::string(); }
    catch(...){
        Base::Base();
        throw;
    }
    try{ data2.std::string::string(); }
    catch(...){
        data1.std::string::~string();
        Base::~Base();
        throw;
    }
    ...
}

Derived的析構函數、Base的構造和析構函數也是一樣的,事實上構造和析構函數會被大量地調用。 如果全部inline的話,這些調用都會被擴展為函數體,勢必會造成目標代碼膨脹。

如果你是庫的設計者,那麼你的接口函數的inline特性的變化將會導致客戶代碼的重新編譯。 因為如果你的接口是inline的,那麼客戶需要將函數體展開編譯到客戶的目標代碼中。

總結

那麼我們應當如何決定是否inline呢?最初我們不應inline任何東西,除非它是必須被inline的或者真的是很顯然(比如前述的age()方法)。 況且只有20%的代碼會決定80%的性能,當我們遇到那20%性能關鍵的部分時再去inline它不遲!

inline函數也是不易調試的。。因為它被inline了。

  • 最小化inline的使用。
  • 不要因為函數模板在頭文件中就要把它inline。

    除非注明,本博客文章均為原創,轉載請以鏈接形式標明本文地址: http://harttle.com/2015/08/28/effective-cpp-30.html

    版權聲明:本文為博主原創文章,轉載請附上原文鏈接。

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved