C++ 的代碼包含頭檔案和實作檔案兩部分, 頭檔案一般是提供給別人使用的, 但是一旦頭檔案發生改變,不管多小的變化,所有參考他的檔案就必須重新編譯,編譯就要花時間,
假如你做的工程比較大(比如二次封裝chrome這類的開發),重新編譯一次的時間就會浪費上班的大部分時間,這樣干了一天挺累的, 但是你的老板說你沒有產出,結果你被fired, 是不是很怨啊,所以...
言歸正傳,怎樣介紹編譯時間呢, 我知道的就3個辦法:
? 洗掉不必要的#include,替代辦法 使用前向宣告 (forward declared )
? 洗掉不必要的一大堆私有成員變數,轉而使用 ”impl” 方法
? 洗掉不必要的類之間的繼承
為了講清楚這3點,還是舉個實體比較好,
現在先假設你找到一個新作業,接手以前某個程式員寫的類,如下:
// 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 classold : publicfile, privatedb { public: old( constcx& ); db get_db( int, char* ); cx get_cx( int, cx ); cx& fun1( db ); error fun2( error ); virtualstd::ostream& print( std::ostream& ) const; private: std::list<cx> cx_list_; deduce deduce_d_; }; inlinestd::ostream& operator<<( std::ostream& os,constold& old_val ) { returnold_val.print(os); }
這個類看完了, 如果你已經看出了問題出在哪里, 接下來的不用看了, 你是高手, 這些基本知識對你來說太小兒科,要是像面試時被問住了愣了一下,請接著看吧
先看怎么使用第一條: 洗掉不必要的#include
這個類參考 5個頭檔案, 那意味著那5個頭檔案所參考的頭檔案也都被參考了進來, 實際上, 不需要參考5 個,只要參考2個就完全可以了
1、洗掉不必要的#include,替代辦法 使用前向宣告 (forward declared )
? 洗掉頭檔案 iostream, 我剛開始學習C++ 時照著《C++ primer》 抄,只要看見關于輸入,輸出就把 iostream 頭檔案加上, 幾年過去了, 現在我知道不是這樣的, 這里只是定義輸出函式, 只要參考ostream 就夠了
? 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頭檔案可以更新如下了:
// 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" classerror; classold : publicfile, privatedb { public: old( constcx& ); db get_db( int, char* ); cx get_cx( int, cx ); cx& fun1( db ); error fun2( error ); virtualstd::ostream& print( std::ostream& ) const; private: std::list<cx> cx_list_; // cx 是模版型別,既不是函式引數型別也不是函式回傳值型別,所以cx.h 頭檔案不能洗掉 deduce deduce_d_; // deduce 是型別定義,也不洗掉他的頭檔案 }; inlinestd::ostream& operator<<( std::ostream& os,constold& old_val ) { returnold_val.print(os); }
到目前為止, 洗掉了一些代碼, 是不是心情很爽,據說看一個程式員的水平有多高, 不是看他寫了多少代碼,而是看他少寫了多少代碼,
如果你對C++ 編程有更深一步的興趣, 接下來的文字你還是會看的,再進一步洗掉代碼, 但是這次要另辟蹊徑了
2、洗掉不必要的一大堆私有成員變數,轉而使用 ”impl” 方法
? 使用 ”impl” 實作方式寫代碼,減少客戶端代碼的編譯依賴
impl 方法簡單點說就是把 類的私有成員變數全部放進一個impl 類, 然后把這個類的私有成員變數只保留一個impl* 指標,
代碼如下:
// file old.h classold { //公有和保護成員 // public and protected members private: //私有成員, 只要任意一個的頭檔案發生變化或成員個數增加,減少,所有參考old.h的客戶端必須重新編譯 // private members; whenever these change, // all client code must be recompiled }; 改寫成這樣: // file old.h classold { //公有和保護成員 // public and protected members private: classoldImpl* pimpl_; // 替換原來的所有私有成員變數為這個impl指標,指標只需要前向宣告就可以編譯通過,這種寫法將前向宣告和定義指標放在了一起, 完全可以, //當然,也可以分開寫 // a pointer to a forward-declared class }; // file old.cpp structoldImpl { //真正的成員變數隱藏在這里, 隨意變化, 客戶端的代碼都不需要重新編譯 // private members; fully hidden, can be // changed at will without recompiling clients }; 改為impl實作后是這樣的: // 只用 file and cx 有虛函式. #include "file.h" #include "db.h" classcx; classerror; classold : publicfile, privatedb { public: old( constcx& ); db get_db( int, char* ); cx get_cx( int, cx ); cx& fun1( db ); error fun2( error ); virtualstd::ostream& print( std::ostream& ) const; private: classoldimpl* pimpl; //此處前向宣告和定義 }; inlinestd::ostream& operator<<( std::ostream& os,constold& old_val ) { returnold_val.print(os); } //implementation file old.cpp classoldimpl{ std::list<cx> cx_list_; deduce dudece_d_; };
3、洗掉不必要的類之間的繼承
面向物件提供了繼承這種機制,但是繼承不要濫用, old class 的繼承就屬于濫用之一, class old 繼承file 和 db 類, 繼承file是公有繼承,繼承db 是私有繼承,繼承file 可以理解, 因為file 中有虛函式, old 要重新定義它,但是根據我們的假設, 只有file 和 cx 有虛函式,私有繼承db 怎么解釋?! 那么唯一可能的理由就是:
通過 私有繼承—讓某個類不能當作基類去派生其他類,類似Java里final關鍵字的功能,但是從實體看,顯然沒有這個用意,所以這個私有繼承完全不必要,應該改用包含的方式去使用db類提供的功能,這樣就可以,
把”db.h”頭檔案洗掉, 把db 的實體也可以放進impl類中,最終得到的類是這樣的:
// 只用 file and cx 有虛函式. #include "file.h" classcx; classerror; classdb; classold : publicfile { public: old( constcx& ); db get_db( int, char* ); cx get_cx( int, cx ); cx& fun1( db ); error fun2( error ); virtualstd::ostream& print( std::ostream& ) const; private: classoldimpl* pimpl; //此處前向宣告和定義 }; inlinestd::ostream& operator<<( std::ostream& os,constold& old_val ) { returnold_val.print(os); } //implementation file old.cpp classoldimpl{ std::list<cx> cx_list_; deduce dudece_d_; };
小結一下:
這篇文章只是簡單的介紹了減少編譯時間的幾個辦法:
1. 洗掉不必要的#include,替代辦法 使用前向宣告 (forward declared )
2. 洗掉不必要的一大堆私有成員變數,轉而使用 ”impl” 方法
3. 洗掉不必要的類之間的繼承
希望這幾條對你有所幫助!

最后,不管你是轉行也好,初學也罷,進階也可,如果你想學編程~
【值得關注】我的 C/C++編程學習交流俱樂部!【點擊進入】
問題答疑,學習交流,技術探討,還有超多編程資源大全,零基礎的視頻也超棒~
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/251336.html
標籤:C++
