青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品

Shuffy

不斷的學習,不斷的思考,才能不斷的進步.Let's do better together!
posts - 102, comments - 43, trackbacks - 0, articles - 19
[轉]http://m.shnenglu.com/tiandejian/archive/2007/11/18/ec_30.html

第30條:     深入探究內聯函數

內聯函數——多么振奮人心的一項發明!它們看上去與函數很相像,它們擁有與函數類似的行為,它們要比宏(參見第 2 條)好用的多,同時你在調用它們時帶來的開銷比一般函數小得多。可謂“內聯在手,別無他求。”

你得到的遠遠比你想象的要多,因為節約函數調用的開銷僅僅是冰山一角。編譯器優化通常是針對那些沒有函數調用的代碼,因此當你編寫內聯函數時,編譯器就會針對函數體的上下文進行優化工作。然而大多數編譯器都不會針對“外聯”函數調用進行優化。

然而,在你的編程生涯中,“沒有免費的午餐”這句生活哲言同樣奏效,內聯函數不會幸免。內聯函數背后蘊含的理念是:用代碼本體來取代每次函數調用,這樣做很可能會是目標代碼的體積增大不少,這一點并不是非要統計學博士才能看得清。對于內存空間有限的機器而言,過分熱衷于使用內聯則會造成函數占用過多的空間。即使在虛擬內存中,那些冗余的內聯代碼也會帶來不少無謂的分頁,從而使緩存讀取命中率降低,最終帶來性能的犧牲。

另一方面,如果一個內聯函數體非常的短,那么為函數體所生成代碼的體積就會比為函數調用生成的代碼小一些。此時,內聯函數才真正做到了減小目標代碼和提高緩存讀取命中率的目的。

我們要時刻保持清醒, Inline 是對編譯器的一次請求,而不是一條命令。這種請求可以顯式提出也可以隱式提出。隱式請求的途徑就是:在類定義的內部定義函數:

class Person {

public:

 ...

 int age() const { return theAge; }// 隱式內聯請求 :

 ...                              // 年齡 age 在類定義中做出定義

 

private:

 int theAge;

};

這樣的函數通常是成員函數,但是類中定義的函數也可以是友元(參見第 46 條),如果函數是友元,那么也應隱式將它們定義為內聯函數。

顯式聲明內聯函數的方法為:在函數定義之前添加 inline 關鍵字。比如說,下面是標準 max 模板(來自 <algorithm> )通常的定義方式:

template<typename T>            // 顯式內聯請求:

inline const T& std::max(const T& a, const T& b)

{ return a < b ? b : a; }       // std::max 的前邊添加 ”inline”

max 是一個模板這一事實,讓我們不免得出這樣的推論:內聯函數和模 板都應該在頭文件中定義。這就使一些程序員做出“函數模板必須為內聯函數”的論斷。這一結論不僅不合法,而且也存在潛在的害處,所以這里我們還是要大略的了解一下。

由于大多數構建環境都是在編譯過程中進行內聯,因此內聯函數一般情況下都應該定義在頭文件中。編譯器必須首先了解函數的大致情況,以便于用所調用函數體來代替這次函數調用。(一些構建環境在連接過程中進行內聯,還有個別基于 .NET 通用語言基礎結構( CLI )的托管環境甚至是在運行時進行內聯。這樣的環境僅僅屬于例外,而不是守則。在大多數 C++ 程序中,內聯是一個編譯時行為。)

模板通常保存在頭文件中,但是編譯器還需要了解模板的大致情形,以便于在用到時進行正確的實例化。(然而,這并不是一成不變的。一些構建環境在連接時進行模板實例化。但是編譯時實例化才是更通用的方式。)

模板實例化相對于內聯是獨立的。如果你正在編寫一個模板,而你又確信由這個模板所實例化出的所有函數都應該是內聯的,那么這個模板就應該添 inline 關鍵字;這也就是上文中 std::max 現的做法。但是如果你正在編寫的模板并不需要實例化內聯函數,那么就不需要聲明內聯模板(無論是顯式還是隱式)。內聯也是有開銷的,不假思索就引入內聯的開銷的做法并不明智。我們已經介紹過了內聯是如何使代碼膨脹起來的(對于模板的作者而言,還應該做更周密的考慮——參見第 44 條),但是內聯還會帶來其他的開銷,這就是下文中我們將要討論的問題。

inline 是對編譯器的一次請求,但編譯器可能會忽略它。在我們的討論開 始之前,我們首先要弄清這一點。大多數編譯器如果認為當前的函數過于復雜(比如包括循環或遞歸的函數),或者這個函數是虛函數(即使是最平常的虛函數調用),就會拒絕將其內聯。后一個結論很好理解。因為 virtual 意味著“等到運行時再指出要調用哪個程序,”而 inline 意味著“在執行程序之前,使用要調用的函數來代替這次調用。”如果編譯器不知道要調用哪個函數,那么它們拒絕內聯函數體的做法就無可厚非了。

綜上所述,我們得出下面的結論:一個給定的函數是否得到內聯,取決于你正在使用的構建環境——主要是編譯器。幸運的是,大多數編譯器擁有診斷機制,如果編譯器在內聯函數時失敗了,那么它們將會做出警告(參見第 53 條)。

有些時候,即使編譯器認為某個函數非常適合進行內聯,可是還是會為它提供一個函數體。舉例說,如果你的程序要取得某個內聯函數的地址,那么編譯器必須用典型的方法為其創建一個外聯的函數體。那么編譯器又怎樣讓一個指針去指向一個不存在的函數呢?再加上編譯器一般不會通過對函數指針的調用進行內聯這一事實,更能肯定這一結論:對于一個內聯函數的調用是否應該得到內聯,取決于這一調用是如何進行的:

inline void f() {...} // 假設編譯器樂意于將 f 的調用進行內聯

 

void (*pf)() = f;      // pf 指向 f

 

...

 

f();                   // 此調用將被內聯,因為這是一次“正常”的調用

 

pf();                     // 此調用很可能不會被內聯,

                       // 因為它是通過一個函數指針進行的

即使你從未使用函數指針,未得到內聯的函數依然“陰魂不散”,這是因為需求函數指針的不僅僅是程序員。比如,編譯器在為對象的數組進行構造或析構時,也會生成構造函數和析構函數的外聯副本,從而使它們可以得到這些函數的指針以便使用。

實際上,為構造函數和析構函數進行內聯通常不是一個好的選擇,這兩者甚至不如一些隨意挑選的“選手”。請看下面示例 Derived 類的構 造函數:

class Base {

public:

 ...

 

private:

   std::string bm1, bm2;     // 基類成員 1 2

};

 

class Derived: public Base {

public:

 Derived() {}                // 派生類的構造函數為空 還有別的可能 ?

 

 ...

 

private:

 std::string dm1, dm2, dm3;// 派生類成員 1–3

};

乍看上去,將這個構造函數進行內聯再適合不過了,因為它不包含任何代碼。其實你的眼睛欺騙了你。

C++ 對于在創建和銷毀對象的過程中發生的事件進行了多方面的保證。比如,當你使用 new 時,你動態創建的對象的構造函數就會自動將其初始化;當你使用 delete 時,將調用相關的析構函數。當你創建一個對象時。每個基類和該對象中的每個數據成員將自動得到構造,在銷毀這個對象時,針對兩者的析構過程將會自動進行。如果在對象的構造過程中有異常拋出,那么對象中已經得到構造的部分將統統被自動銷毀。在所有這些場景中, C++ 告訴你什么一定會發生,但它沒有說明如何發生。這一點取決于編譯器的實現者,但是必須要清楚的一點是,這些事情并不是自發的。你必須要在程序中添加一些代碼來實現它們。這些代碼一定存在于某處,它們由編譯器代勞,用于在編譯過程中插入你的程序中。一些時候它們就存在于構造函數和析構函數中,所以,對于上文中 Derived 的空構造函數,我們可以將具體實現中生成的代碼等價看作:

Derived::Derived()                  // Derived 空構造函數的抽象實現

{

 

  Base::Base();                        // 初始化 Base 部分

 

  try { dm1.std::string::string(); }  // 嘗試構造 dm1

 catch (...) {                        // 如果拋出異常 ,

   Base::~Base();                     // 銷毀基類部分 ,

   throw;                             // 并且傳播該異常

  }

 

  try { dm2.std::string::string(); }  // 嘗試構造 dm2

 catch(...) {                         // 如果拋出異常 ,

   dm1.std::string::~string();        // 銷毀 dm1,

   Base::~Base();                     // 銷毀基類部分 ,

   throw;                             // 并且傳播該異常

 }

 

 try { dm3.std::string::string(); } // 嘗試構造 dm3

 catch(...) {                         // 如果拋出異常 ,

   dm2.std::string::~string();        // 銷毀 dm2,

   dm1.std::string::~string();        // 銷毀 dm1,

  Base::~Base();                     // 銷毀基類部分 ,

   throw;                             // 并且傳播該異常

 }

}

這段代碼并不能完全真實反映出編譯器所做的事情,因為真實的編譯器采用的做法更加復雜。然而,上面的代碼可以較為精確地反映出 Derived 的“空”構造函數必須要提供的內容。無論編譯器處理異常的實現方式多么復雜, Derived 的構造函數必須至少為其數據成員和基類調用構造函數,這些調用(可能就是內聯的)會使 Derived 顯得不那么適合進行內聯。

一推理過程對于 Base 的構造函數同樣適用,因此如果將 Base 內聯,所有添加進其中的代碼同樣也會添加進 Derived 的構造函數中(通過 Derived 構造函數調用 Base 構造函數的過程)。同時,如果 string 的構造函數恰巧被內聯了,那么 Derived 的構造函數將為其復制出五份副本,分別對應 Derived 對象中包含的五個字符串(兩個繼承而來,另外三個系對象本身包括)。現在,“ Derived 的構造函數是否應該內聯不是一個純機械化問題”就很容易理解了。對于 Derived 的析構函數也一樣,你必須親自關注 Derived 的構造函數初始化的對象是否全部恰當的得到銷毀,這一點機器無法代替。

庫設計者必須估算出將函數內聯所帶來的影響,因為你根本無法為庫中客戶端程序員可見的內聯函數提供底層的升級。換句話說,如果 f 是庫中的一個內聯函數,那么庫的客戶端程序員就會將 f 的函數體編譯進他們的程序中。隨后,如果一個庫實現者修改了 f 的內容,那么所有曾經使用過 f 的客戶端程序員必須要重新編譯他們的代碼。這一點是我們所不希望看到的。另一個角度講,如果 f 不是內聯函數,那么修改 f 只需要客戶端程序員重新連接一下就可以了。這樣要比重新編譯減少很多繁雜的工作,并且,如果庫中需要使用的函數是動態鏈接的,那么它對于客戶端程序員就是完全透明的。

我們的目標是開發優質的程序,因此要將這些重要問題牢記在心。但是以編寫代碼實際操作的角度來說,這一個事實將淹沒一切:大多數調試人員面對內聯函數時會遇到麻煩。這并不會令人意外,因為你無法為一個尚不存在的函數設定一個跟蹤點。一些構建環境試圖支持內聯函數的調試,但是幾乎都失敗了,大多數環境都是在調試過程中直接禁止內聯。

對于“哪個函數應該聲明為 inline 而哪些不應該”這一問題,我們可以由上文中引出一個邏輯上的策略。起初,不要內聯任何內容,或者僅挑選出那些不得不內聯的函數(參見第 46 條)或者那些確實是很細小的程序(比如本節開篇處出現的 Person::age )進行內聯。謹慎引入內聯,你就為調試工作提供了方便,但是你仍然要為內聯擺正位置:它屬于手工的優化操作。不要忘記 80-20 經驗決定主義原則:一個典型的程序將花去 80% 的時間僅僅運行 20% 的代碼。這是一個非常重要的原則,因為它時時刻刻提醒我們,軟件開發者的目標是:找出你的代碼中 20% 的這部分進行優化,從而從整體上提高程序的性能。你可以花費很長的時間進行內聯、修改函數等等,但如果你沒有鎖定正確的目標,那么你做再多的努力也是徒勞。

銘記在心

僅僅對小型的、調用頻率高的程序進行內聯。這將簡化你的調試操作,為底層更新提供方便,降低潛在的代碼膨脹發生的可能,并且可以讓程序獲得更高的速度。

不要將模板聲明為 inline 的,因為它們一般在頭文件中出現。

青青草原综合久久大伊人导航_色综合久久天天综合_日日噜噜夜夜狠狠久久丁香五月_热久久这里只有精品
  • <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>
            日韩一级精品视频在线观看| 亚洲三级性片| 久久看片网站| 性刺激综合网| 久久久久国色av免费看影院 | 欧美在线看片a免费观看| 午夜精品99久久免费| 欧美在线日韩| 久久精品国产亚洲一区二区三区| 欧美一二三区精品| 美女视频黄a大片欧美| 欧美黄色日本| 欧美三区在线视频| 欧美成人免费一级人片100| 欧美成人精品影院| 欧美精品在线免费| 欧美大胆人体视频| 国产精品久久久久久户外露出| 国产精品v欧美精品v日本精品动漫| 欧美日韩精品中文字幕| 国产精品自在欧美一区| 国内精品国产成人| 亚洲精品九九| 亚洲欧美成人在线| 久久综合色88| 99在线精品视频在线观看| 午夜伦理片一区| 欧美激情一区二区三区高清视频| 国产精品激情av在线播放| 一区二区视频免费在线观看 | 免费永久网站黄欧美| 国产精品国产自产拍高清av王其| 狠狠色丁香久久婷婷综合_中| 亚洲国产精品国自产拍av秋霞| 亚洲尤物在线视频观看| 久久蜜桃av一区精品变态类天堂| 欧美韩国在线| 欧美在线视频免费播放| 欧美区在线观看| 亚洲国产女人aaa毛片在线| 性久久久久久| 亚洲精品乱码久久久久久日本蜜臀 | 亚洲国产精品成人久久综合一区| 午夜综合激情| 日韩午夜av| 暖暖成人免费视频| 在线观看亚洲| 久久天天躁狠狠躁夜夜爽蜜月| aaa亚洲精品一二三区| 欧美国产欧美亚州国产日韩mv天天看完整 | 免费久久99精品国产自在现线| 亚洲欧美国产不卡| 欧美日韩一区二区三区四区在线观看 | 久久精品亚洲精品国产欧美kt∨| 日韩亚洲视频| 欧美极品在线播放| 最新中文字幕一区二区三区| 亚洲国产精品小视频| 亚洲欧美一区二区视频| 国产精品视频不卡| 午夜精品久久久久影视| 99国内精品| 欧美私人网站| 亚洲欧美怡红院| 亚洲一区二区三区在线播放| 欧美日韩三级在线| 在线一区免费观看| 一本色道久久综合精品竹菊| 欧美视频成人| 欧美亚洲专区| 欧美在线视频免费播放| 在线观看日韩av| 亚洲丁香婷深爱综合| 欧美xx69| 亚洲专区一区二区三区| 亚洲综合色视频| 国模吧视频一区| 麻豆精品国产91久久久久久| 欧美成人午夜剧场免费观看| 一区二区电影免费在线观看| 亚洲一级在线观看| 国内精品国产成人| 亚洲国产裸拍裸体视频在线观看乱了中文| 欧美成人免费在线观看| 亚洲一区二区三区国产| 欧美在线网址| 亚洲精品社区| 亚洲欧美精品伊人久久| 在线观看欧美亚洲| 99re视频这里只有精品| 国产色产综合产在线视频| 欧美高清在线| 国产精品亚洲片夜色在线| 久久只精品国产| 欧美日韩一区二区在线播放| 久久久久久九九九九| 欧美日韩岛国| 美女国内精品自产拍在线播放| 欧美日韩情趣电影| 久久尤物视频| 欧美啪啪一区| 久久蜜桃香蕉精品一区二区三区| 欧美精品麻豆| 久久视频一区二区| 欧美日韩一区二区三区在线观看免 | 午夜精品一区二区三区电影天堂 | 麻豆精品国产91久久久久久| 亚洲无线一线二线三线区别av| 欧美在线免费视屏| 亚洲婷婷综合久久一本伊一区| 久久久久国产一区二区三区四区| 一区二区av在线| 久久天堂成人| 看片网站欧美日韩| 欧美系列亚洲系列| 亚洲国产精品视频| 狠狠入ady亚洲精品经典电影| 亚洲一区二区免费| 一区二区三区高清| 美女视频网站黄色亚洲| 欧美尤物巨大精品爽| 欧美日韩国产黄| 亚洲国产cao| 一区二区三区在线高清| 欧美一区二区成人| 亚洲欧美日韩成人高清在线一区| 欧美日韩爆操| 最新日韩在线视频| 亚洲激情精品| 美女被久久久| 欧美国产日韩一区二区| 亚洲第一区在线观看| 久久精品国产亚洲5555| 久久精品夜色噜噜亚洲a∨| 国产精品视频成人| 亚洲一区精品在线| 午夜在线成人av| 国产精品美女久久久久久免费| 一级成人国产| 午夜精品久久久久久99热| 国产精品国产a| 亚洲图片在线| 久久精品一级爱片| 怡红院av一区二区三区| 久久精品亚洲一区二区三区浴池| 久久久久九九九九| 亚洲成色777777在线观看影院| 久久影视三级福利片| 亚洲风情在线资源站| 99在线精品视频| 欧美日韩免费一区| 亚洲专区一二三| 久久亚洲色图| 最近中文字幕日韩精品| 欧美日韩国产在线播放| 99re亚洲国产精品| 午夜精品久久久久久久白皮肤 | 美日韩精品免费观看视频| 亚洲国产欧美日韩精品| 99re热这里只有精品视频| 欧美日韩精品不卡| 亚洲欧美中日韩| 欧美好骚综合网| 亚洲一二三级电影| 国产偷自视频区视频一区二区| 久久视频在线看| 99精品福利视频| 久久久久88色偷偷免费| 91久久精品国产91久久| 欧美日韩另类一区| 欧美一区二区三区电影在线观看| 男男成人高潮片免费网站| 一区二区三区 在线观看视频| 国产精品一区一区三区| 久久人人爽人人爽爽久久| 99国产麻豆精品| 久久久噜噜噜久久| 一本久久综合亚洲鲁鲁| 韩国成人精品a∨在线观看| 欧美日韩国产欧| 久久精品国产综合精品| 欧美欧美天天天天操| 亚洲精品乱码久久久久久按摩观| 亚洲欧美日韩精品综合在线观看| 一区在线视频| 国产麻豆91精品| 欧美不卡视频| 欧美在线观看www| 一本大道久久a久久综合婷婷| 欧美sm视频| 久久国产88| 亚洲天堂成人在线观看| 18成人免费观看视频| 国产色综合网| 欧美日韩一区不卡| 欧美国产精品中文字幕| 久久久久综合| 欧美在线网站| 欧美一区二区性| 亚洲欧美制服中文字幕|