減少C++代碼編譯時間的方法,編譯

來源:互聯網
上載者:User

減少C++代碼編譯時間的方法,編譯

c++ 的程式碼封裝含標頭檔和實現檔案兩部分, 標頭檔一般是提供給別人(也叫客戶)使用的, 但是一旦標頭檔發生改變,不管多小的變化,所有引用他的檔案就必須重新編譯,編譯就要花時間,假如你做的工程比較大(比如二次封裝chrome這類的開發),重新編譯一次的時間就會浪費上班的大部分時間,這樣幹了一天挺累的, 但是你的老闆說你沒有產出,結果你被fired, 是不是很怨啊, 如果你早點看到這段文章,你就會比你的同事開發效率高那麼一些,早日在c++上成為大牛,就可以理直氣壯地去幹你想幹可以乾的事,是c++大牛就來看看這個:http://t.cn/RZDuuIz   那樣被fired就不會是你了,你說這篇文章是不是價值千金!開個玩笑 :)

言歸正傳,怎樣減少編譯時間呢, 我知道的就3個辦法:

1. 刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )

2. 刪除不必要的一大堆私人成員變數,轉而使用 “impl” 方法

3. 刪除不必要的類之間的繼承

為了講清楚這3點,還是舉個執行個體比較好,這個執行個體我會一步一步的改進(因為我也是一點一點摸索出來了,如果哪裡說錯了, 你就放心的噴吧,我會和你在爭論到底的,呵呵)

現在先假設你找到一個新工作,接手以前某個程式員寫的類,如下:

<span style="font-family:Arial;font-size:14px;">//  old.h: 這就是你接收的類//#include <iostream>#include <ostream>#include <list> // 5 個 分別是file , db, cx, deduce or error , 水平有限沒有模板類// 只用 file and cx 有虛函數.#include "file.h"  // class file#include "db.h"  // class db#include "cx.h"  // class cx#include "deduce.h"  // class deduce#include "error.h"  // class error class old : public file, private db {public:  old( const cx& );  db  get_db( int, char* );  cx  get_cx( int, cx );  cx& fun1( db );  error  fun2( error );  virtual std::ostream& print( std::ostream& ) const;private:  std::list<cx> cx_list_;  deduce       deduce_d_;};inline std::ostream& operator<<( std::ostream& os,const old& old_val ){ return old_val.print(os); }</span>

這個類看完了, 如果你已經看出了問題出在哪裡, 接下來的不用看了, 你是高手, 這些基本知識對你來說太小兒科,要是像面試時被問住了愣了一下,請接著看吧

先看怎麼使用第一條: 刪除不必要的#include

這個類引用 5個標頭檔, 那意味著那5個標頭檔所引用的標頭檔也都被引用了進來, 實際上, 不需要引用5 個,只要引用2個就完全可以了

1.刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )

1.1刪除標頭檔 iostream, 我剛開始學習c++ 時照著《c++ primer》 抄,只要看見關於輸入,輸出就把 iostream 標頭檔加上, 幾年過去了, 現在我知道不是這樣的, 這裡只是定義輸出函數, 只要引用ostream 就夠了

1.2.ostream標頭檔也不要, 替換為 iosfwd , 為什麼, 原因就是, 參數和傳回型別只要前向聲明就可以編譯通過, 在iosfwd 檔案裡 678行(我的環境是vs2013,不同的編譯環境具體位置可能會不相同,但是都有這句聲明) 有這麼一句

typedef basic_ostream<char, char_traits<char> > ostream;

inline std::ostream& operator<<( std::ostream& os,const old& old_val )

{ return old_val.print(os); }

除此之外,要是你說這個函數要操作ostream 對象, 那還是需要#include <ostream> , 你只說對了一半, 的確, 這個函數要操作ostream 對象, 但是請看他的函數實現,

裡面沒有定義一個類似 std::ostream os, 這樣的語句,話說回來,但凡出現這樣的定義語句, 就必須#include 相應的標頭檔了 ,因為這是請求編譯器分配空間,而如果只前向聲明 class XXX; 編譯器怎麼知道分配多大的空間給這個對象!

看到這裡, old.h標頭檔可以更新如下了:

<span style="font-family:Arial;font-size:14px;">//  old.h: 這就是你接收的類//#include <iosfwd>  //新替換的標頭檔#include <list> // 5 個 分別是file , db, cx, deduce or error, 水平有限沒有模板類// 只用 file and cx 有虛函數.#include "file.h"  // class file,作為基類不能刪除,                   // 刪除了編譯器就不知道執行個體化old 對象時分配多大的空間了#include "db.h"  // class db,作為基類不能刪除,同上#include "cx.h"  // class cx#include "deduce.h"  // class deduce// error 只被用做參數和傳回值類型, 用前向聲明替換#include "error.h" class error;  class old : public file, private db {public:  old( const cx& );  db  get_db( int, char* );  cx  get_cx( int, cx );  cx& fun1( db );  error  fun2( error );  virtual std::ostream& print( std::ostream& ) const;private:  std::list<cx> cx_list_; //  cx 是模版類型,既不是函數參數類型                          //  也不是函數傳回值類型,所以cx.h 標頭檔不能刪除  deduce       deduce_d_; //  deduce 是類型定義,也不刪除他的標頭檔};inline std::ostream& operator<<( std::ostream& os,const old& old_val ){ return old_val.print(os); }</span>

到目前為止, 刪除了一些代碼, 是不是心情很爽,據說看一個程式員的水平有多高, 不是看他寫了多少代碼,而是看他少寫了多少代碼。

如果你對C++ 編程有更深一步的興趣, 接下來的文字你還是會看的,再進一步刪除代碼, 但是這次要另闢蹊徑了

2. 刪除不必要的一大堆私人成員變數,轉而使用 “impl” 方法

2.1.使用 “impl” 實現方式寫代碼,減少用戶端代碼的編譯依賴

impl 方法簡單點說就是把 類的私人成員變數全部放進一個impl 類, 然後把這個類的私人成員變數只保留一個impl* 指標,代碼如下:

<span style="font-family:Arial;font-size:14px;">// file old.hclass old {  // 公有和保護成員  // public and protected membersprivate:  // 私人成員, 只要任意一個的標頭檔發生變化或成員個數增加,  // 減少,所有引用old.h的用戶端必須重新編譯  // private members; whenever these change,  // all client code must be recompiled};</span>

改寫成這樣:

<span style="font-family:Arial;font-size:14px;">// file old.hclass old {  // 公有和保護成員  // public and protected membersprivate:  class oldImpl* pimpl_;  //  替換原來的所有私人成員變數為這個impl指標,指標只需要前向聲明就可以編譯通過,  //  這種寫法將前向聲明和定義指標放在了一起,完全可以。  //  當然,也可以分開寫  //  a pointer to a forward-declared class}; // file old.cppstruct oldImpl {  // 真正的成員變數隱藏在這裡, 隨意變化, 用戶端的代碼都不需要重新編譯  // private members; fully hidden, can be  // changed at will without recompiling clients};</span>

不知道你看明白了沒有, 看不明白請隨便寫個類實驗下,我就是這麼做的,當然凡事也都有優缺點,下面簡單對比下:


使用impl 實作類別 不使用impl實作類別
優點 類型定義與用戶端隔離, 減少#include 的次數,提高編譯速度,庫端的類隨意修改,用戶端不需要重新編譯 直接,簡單明了,不需要考慮堆分配,釋放,記憶體流失問題
缺點 對於impl的指標必須使用堆分配,堆釋放,時間長了會產生記憶體片段,最終影響程式運行速度, 每次調用一個成員函數都要經過impl->xxx()的一次轉寄 庫端任意標頭檔發生變化,用戶端都必須重新編譯

改為impl實現後是這樣的:

<span style="font-family:Arial;font-size:14px;">// 只用 file and cx 有虛函數.#include "file.h" #include "db.h" class cx;class error; class old : public file, private db {public:  old( const cx& );  db  get_db( int, char* );  cx  get_cx( int, cx );  cx& fun1( db );  error  fun2( error );  virtual std::ostream& print( std::ostream& ) const;private:class oldimpl* pimpl; //此處前向聲明和定義};inline std::ostream& operator<<( std::ostream& os,const old& old_val ){ return old_val.print(os); } //implementation file old.cppclass oldimpl{std::list<cx> cx_list_;deduce        dudece_d_;};</span>

3. 刪除不必要的類之間的繼承

物件導向提供了繼承這種機制,但是繼承不要濫用, old class 的繼承就屬於濫用之一, class old 繼承file 和 db 類, 繼承file是公有繼承,繼承db 是私人繼承

,繼承file 可以理解, 因為file 中有虛函數, old 要重新定義它, 但是根據我們的假設, 只有file 和 cx 有虛函數,私人繼承db 怎麼解釋?! 那麼唯一可能的理由就是:

通過 私人繼承—讓某個類不能當作基類去派生其他類,類似Java裡final關鍵字的功能,但是從執行個體看,顯然沒有這個用意, 所以這個私人繼承完全不必要, 應該改用包含的方式去使用db類提供的功能, 這樣就可以

把”db.h”標頭檔刪除, 把db 的執行個體也可以放進impl類中,最終得到的類是這樣的:

<span style="font-family:Arial;font-size:14px;">// 只用 file and cx 有虛函數.#include "file.h" class cx;class error;class db;class old : public file {public:  old( const cx& );  db  get_db( int, char* );  cx   get_cx( int, cx );  cx& fun1( db );  error  fun2( error );  virtual std::ostream& print( std::ostream& ) const;private:  class oldimpl* pimpl; //此處前向聲明和定義 }; inline std::ostream& operator<<( std::ostream& os,const old& old_val ) { return old_val.print(os); } //implementation file old.cppclass oldimpl{std::list<cx> cx_list_;deduce        dudece_d_;};</span>

小結一下:

這篇文章只是簡單的介紹了減少編譯時間的幾個辦法:

1. 刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )

2. 刪除不必要的一大堆私人成員變數,轉而使用 “impl” 方法

3. 刪除不必要的類之間的繼承

此篇文章旨在跟大家共同分享進步,哦,不,加薪!呵呵,想要加薪,更高薪資,看這裡:http://t.cn/RZDuuIz

保證你不看一定會後悔,看了一定不會後悔!


聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.