程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 減少C++代碼編譯時間的方法,編譯

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

編輯:C++入門知識

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


  c++ 的代碼包含頭文件實現文件兩部分, 頭文件一般是提供給別人(也叫客戶)使用的, 但是一旦頭文件發生改變,不管多小的變化,所有引用他的文件必須重新編譯,編譯就要花時間,假如你做的工程比較大(比如二次封裝chrome這類的開發),重新編譯一次的時間就會浪費上班的大部分時間,這樣干了一天挺累的, 但是你的老板說你沒有產出,結果你被fired, 是不是很怨啊, 如果你早點看到這段文章,你就會比你的同事開發效率高那麼一些,那樣被fired就不會是你了,你說這篇文章是不是價值千金!開個玩笑 :),言歸正傳,怎樣減少編譯時間呢, 我知道的就3個辦法:

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

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

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

  為了講清楚這3點,還是舉個實例比較好,這個實例我會一步一步的改進(因為我也是一點一點摸索出來了,如果哪裡說錯了, 你就放心的噴吧,我會和你在爭論到底的,呵呵),現在先假設你找到一個新工作,接手以前某個程序員寫的類,如下:

1 // old.h: 這就是你接收的類 2 // 3 #include <iostream> 4 #include <ostream> 5 #include <list> 6 7 // 5 個 分別是file , db, cx, deduce or error , 水平有限沒有模板類 8 // 只用 file and cx 有虛函數. 9 #include "file.h" // class file 10 #include "db.h" // class db 11 #include "cx.h" // class cx 12 #include "deduce.h" // class deduce 13 #include "error.h" // class error 14 15 class old : public file, private db { 16 public: 17 old( const cx& ); 18 db get_db( int, char* ); 19 cx get_cx( int, cx ); 20 cx& fun1( db ); 21 error fun2( error ); 22 virtual std::ostream& print( std::ostream& ) const; 23 private: 24 std::list<cx> cx_list_; 25 deduce deduce_d_; 26 }; 27 inline std::ostream& operator<<( std::ostream& os,const old& old_val ) 28 { return old_val.print(os); } View Code

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

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

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

1.1刪除不必要的#include

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

1.2刪除頭文件 iostream

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

1.3刪除ostream頭文件

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

1 typedef basic_ostream<char, char_traits<char> > ostream; 2 3 inline std::ostream& operator<<( std::ostream& os,const old& old_val ) 4 5 { return old_val.print(os); } View Code

  除此之外,要是你說這個函數要操作ostream對象,那還是需要#include <ostream> , 你只說對了一半,的確,這個函數要操作ostream 對象, 但是請看他的函數實現,裡面沒有定義一個類似 std::ostream os,這樣的語句,話說回來,但凡出現這樣的定義語句, 就必須#include相應的頭文件了,因為這是請求編譯器分配空間,而如果只前向聲明 class XXX; 編譯器怎麼知道分配多大的空間給這個對象!看到這裡, old.h頭文件可以更新如下了:

1 // old.h: 這就是你接收的類 2 // 3 #include <iosfwd> //新替換的頭文件 4 #include <list> 5 6 // 5 個 分別是file , db, cx, deduce or error, 水平有限沒有模板類 7 // 只用 file and cx 有虛函數. 8 #include "file.h" // class file,作為基類不能刪除, 9 10 // 刪除了編譯器就不知道實例化old 對象時分配多大的空間了 11 #include "db.h" // class db,作為基類不能刪除,同上 12 #include "cx.h" // class cx 13 #include "deduce.h" // class deduce 14 // error 只被用做參數和返回值類型, 用前向聲明替換#include "error.h" 15 class error; 16 17 class old : public file, private db { 18 public: 19 old( const cx& ); 20 db get_db( int, char* ); 21 cx get_cx( int, cx ); 22 cx& fun1( db ); 23 error fun2( error ); 24 virtual std::ostream& print( std::ostream& ) const; 25 private: 26 std::list<cx> cx_list_; 27 // cx 是模版類型,既不是函數參數類型 28 29 // 也不是函數返回值類型,所以cx.h 頭文件不能刪除 30 deduce deduce_d_; 31 // deduce 是類型定義,也不刪除他的頭文件 32 }; 33 inline std::ostream& operator<<( std::ostream& os,const old& old_val ) 34 { return old_val.print(os); } View Code

  到目前為止, 刪除了一些代碼, 是不是心情很爽,據說看一個程序員的水平有多高, 不是看他寫了多少代碼,而是看他少寫了多少代碼。如果你對C++ 編程有更深一步的興趣, 接下來的文字你還是會看的,再進一步刪除代碼, 但是這次要另辟蹊徑了。

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

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

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

1 // file old.h 2 class old { 3 4 // 公有和保護成員 5 6 // public and protected members 7 private: 8 9 // 私有成員, 只要任意一個的頭文件發生變化或成員個數增加, 10 11 // 減少,所有引用old.h的客戶端必須重新編譯 12 13 // private members; whenever these change, 14 15 // all client code must be recompiled 16 }; View Code

  改寫成這樣:

1 // file old.h 2 class old { 3 4 // 公有和保護成員 5 6 // public and protected members 7 private: 8 class oldImpl* pimpl_; 9 10 // 替換原來的所有私有成員變量為這個impl指針,指針只需要前向聲明就可以編譯通過, 11 12 // 這種寫法將前向聲明和定義指針放在了一起,完全可以。 13 14 // 當然,也可以分開寫 15 16 // a pointer to a forward-declared class 17 }; 18 19 // file old.cpp 20 struct oldImpl { 21 22 // 真正的成員變量隱藏在這裡, 隨意變化, 客戶端的代碼都不需要重新編譯 23 24 // private members; fully hidden, can be 25 26 // changed at will without recompiling clients 27 }; View Code

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

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

  改為impl實現後是這樣的:

1 // 只用 file and cx 有虛函數. 2 #include "file.h" 3 #include "db.h" 4 class cx; 5 class error; 6 7 class old : public file, private db { 8 public: 9 old( const cx& ); 10 db get_db( int, char* ); 11 cx get_cx( int, cx ); 12 cx& fun1( db ); 13 error fun2( error ); 14 virtual std::ostream& print( std::ostream& ) const; 15 private: 16 class oldimpl* pimpl; 17 //此處前向聲明和定義 18 }; 19 inline std::ostream& operator<<( std::ostream& os,const old& old_val ) 20 { return old_val.print(os); } 21 22 //implementation file old.cpp 23 class oldimpl{ 24 std::list<cx> cx_list_; 25 deduce dudece_d_; 26 }; View Code

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

  面向對象提供了繼承這種機制,但是繼承不要濫用,old class的繼承就屬於濫用之一,class old 繼承file和db 類,繼承file是公有繼承,繼承db是私有繼承,繼承file可以理解,因為file中有虛函數,old要重新定義它,但是根據我們的假設,只有file和cx有虛函數,私有繼承db怎麼解釋?! 那麼唯一可能的理由就是:

  通過私有繼承—讓某個類不能當作基類去派生其他類,類似Java裡final關鍵字的功能,但是從實例看,顯然沒有這個用意,所以這個私有繼承完全不必要,應該改用包含的方式去使用db類提供的功能, 這樣就可以把”db.h”頭文件刪除,把db的實例也可以放進impl類中,最終得到的類是這樣的:

1 // 只用 file and cx 有虛函數. 2 #include "file.h" 3 class cx; 4 class error; 5 class db; 6 class old : public file { 7 public: 8 old( const cx& ); 9 db get_db( int, char* ); 10 cx get_cx( int, cx ); 11 cx& fun1( db ); 12 error fun2( error ); 13 virtual std::ostream& print( std::ostream& ) const; 14 private: 15 class oldimpl* pimpl; 16 //此處前向聲明和定義 17 }; 18 inline std::ostream& operator<<( std::ostream& os,const old& old_val ) 19 { return old_val.print(os); } 20 21 //implementation file old.cpp 22 class oldimpl{ 23 std::list<cx> cx_list_; 24 deduce dudece_d_; 25 }; View Code

小結

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

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

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

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

 

原文鏈接:http://blog.jobbole.com/85275/

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