(一)
inline函數,可以調用它們而又不需蒙受函數調用所招致的額外開銷。
inline函數背後的整體觀念是,將“對此函數的每一個調用”都已函數本體替換之,這樣做可能增加你的目標碼(object code)大小。在內存有限的機器上,過度inline會造成程序體積太大,導致換頁行為,降低緩存的命中率等一些帶來效率損失的行為。如果inline函數的本體很小,編譯器針對“函數本體”所產生的碼可能比針對“函數調用”所產出的碼更小。將函數inline可以導致更小的目標碼,從而提高效率。
(二)
inline只是對編譯器的一個申請,不是強制命令。這種申請可以隱喻提出也可以明確提出。
(1)隱喻提出(隱喻方式是將函數定義於class定義式內):
class Person { public: ... int age() const {return theAge;}//一個隱喻的inline申請 ... private: int theAge; };(2)明確提出(明確申請inline函數的做法是在其定義式前加上關鍵字inline):
templateinline const T& std::max(const T& a, const T& b) { return a < b? b: a; }
(三)
(1)如果我們正在寫一個template而我們認為素有根據此template具現出來的函數都應該inlined,那麼請將此template聲明為inline;如果template沒有理由要求它所具現的每個函數都是inlined,就應該避免將這個template聲明為inline(不論顯式還是隱式)。inline需要成本。
(2)大部分編譯器拒絕將太過復雜(例如帶有循環或遞歸)的函數inlining,而所有對virtual函數的調用也都會使inlining落空。因為virtual意味著“等待,直到運行期才確定調用哪個函數”,而inline意味“執行前,先將調用動作替換為被調用函數的本體”。如果編譯器不知道該調用哪個函數,那肯定就沒法inlining了!
(3)有時,雖然編譯器有意願inlining某個函數,但還是可能為該函數生成一個函數本體。例如,如果程序要取某個inline函數的地址,因為編譯器通常必須為此函數生成一個outlined函數本體(畢竟編譯器沒有能力提出一個指針指向並不存在的函數),所以編譯器通常不對“通過函數指針而進行的調用”實施inlining。
inline void f(){…} //假設編譯器有意願inline“對f的調用” void (*pf)() = f; f();//這個調用將被inlined,因為是一個正常調用 pf();//這個調用或許不被inlined,因為通過指針達成
class base { public: ... private: std::string bm1, bm2; }; class Derived : public Base { public: Derived(){ } //Derived 構造函數是空的 是嗎? ... private: std::string dm1, dm2, dm3; };
這個構造函數看起來是inlining的絕佳候選人,因為他根本不含任何代碼,但是:
C++對於“對象被創建和被銷毀時發生什麼事”做了各式各樣的保證。編譯器為稍早說的那個表面上看起來是空的Derived構造函數所產生的代碼,相當於以下所列:
Derived::Derived() { Base::Base(); try{dm1.std::string::string();} catch(...){ Base::~Base(); throw; } try{dm2.std::string::string();} catch(...){ dm1.std::string::~string(); Base::~Base(); throw; } try{dm3.std::string::string();} catch(...){ dm2.std::string::~string(); dm1.std::string::~string(); Base::~Base(); throw; } }這段代碼並不能代表編譯器真正制造出來的代碼,因為真正的編譯器會以更精致復雜的做法來處理異常.盡管如此,這已能准確反映Derived的空白構造函數必須提供的行為。Derived構造函數至少一定會陸續調用其成員變量和baseclass兩者的構造函數,而那些調用(它們自身也可能被inlined)會影響編譯器是否對此空白函數inlining。
(五)
程序庫設計者必須評估"將函數聲明為inline"的沖擊:inline函數無法隨著程序庫的升級而升級。
客戶將“f函數本體”編進其程序中,一旦程序庫設計者決定改變f,所有用到f的客戶端程序都必須重新編譯。然而若f是non-inline函數,客戶端只要重新連接就好了,如果是程序庫采用動態鏈接,升級後的函數甚至可以不知不覺的被應用程序吸納。
請記住:
(1)將大多數inlining限制在小型,被頻繁調用的函數身上.這可使日後的調試過程和二進制升級更容易。也可使潛在的代碼膨脹問題最小化,使程序的速度提升機會最大化。
(2) 不要因為function templates出現在頭文件,就將它們聲明為inline。