您好,登錄后才能下訂單哦!
c++ 的代碼包含頭文件和實(shí)現(xiàn)文件兩部分, 頭文件一般是提供給別人(也叫客戶)使用的, 但是一旦頭文件發(fā)生改變,不管多小的變化,所有引用他的文件就必須重新編譯,編譯就要花時間,假如你做的工程比較大(比如二次封裝chrome這類的開發(fā)),重新編譯一次的時間就會浪費(fèi)上班的大部分時間,這樣干了一天挺累的, 但是你的老板說你沒有產(chǎn)出,結(jié)果你被fired, 是不是很怨啊, 如果你早點(diǎn)看到這段文章,你就會比你的同事開發(fā)效率高那么一些,那樣被fired就不會是你了,你說這篇文章是不是價值千金!開個玩笑 :)
言歸正傳,怎樣介紹編譯時間呢, 我知道的就3個辦法:
1. 刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )
2. 刪除不必要的一大堆私有成員變量,轉(zhuǎn)而使用 "impl" 方法
3. 刪除不必要的類之間的繼承
為了講清楚這3點(diǎn),還是舉個實(shí)例比較好,這個實(shí)例我會一步一步的改進(jìn)(因?yàn)槲乙彩且稽c(diǎn)一點(diǎn)摸索出來了,如果哪里說錯了, 你就放心的噴吧,我會和你在爭論到底的,呵呵)
現(xiàn)在先假設(shè)你找到一個新工作,接手以前某個程序員寫的類,如下
// old.h: 這就是你接收的類 // #include <iostream> #include <ostream> #include <list> // 5 個 分別是file , db, cx, deduce or error , 水平有限沒有模板類 // 只用 file and cx 有虛函數(shù). #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); }
這個類看完了, 如果你已經(jīng)看出了問題出在哪里, 接下來的不用看了, 你是高手, 這些基本知識對你來說太小兒科,要是像面試時被問住了愣了一下,請接著看吧
先看怎么使用第一條: 刪除不必要的#include
這個類引用 5個頭文件, 那意味著那5個頭文件所引用的頭文件也都被引用了進(jìn)來, 實(shí)際上, 不需要引用5 個,只要引用2個就完全可以了
1.刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )
1.1刪除頭文件 iostream, 我剛開始學(xué)習(xí)c++ 時照著《c++ primer》 抄,只要看見關(guān)于輸入,輸出就把 iostream 頭文件加上, 幾年過去了, 現(xiàn)在我知道不是這樣的, 這里只是定義輸出函數(shù), 只要引用ostream 就夠了
1.2.ostream頭文件也不要, 替換為 iosfwd , 為什么, 原因就是, 參數(shù)和返回類型只要前向聲明就可以編譯通過, 在iosfwd 文件里 678行(我的環(huán)境是vs2013,不同的編譯環(huán)境具體位置可能會不相同,但是都有這句聲明) 有這么一句
typedef basic_ostream<char, char_traits<char> > ostream; inline std::ostream& operator<<( std::ostream& os,const old& old_val ) { return old_val.print(os); }
除此之外,要是你說這個函數(shù)要操作ostream 對象, 那還是需要#include <ostream> , 你只說對了一半, 的確, 這個函數(shù)要操作ostream 對象, 但是請看他的函數(shù)實(shí)現(xiàn),
里面沒有定義一個類似 std::ostream os, 這樣的語句,話說回來,但凡出現(xiàn)這樣的定義語句, 就必須#include 相應(yīng)的頭文件了 ,因?yàn)檫@是請求編譯器分配空間,而如果只前向聲明 class XXX; 編譯器怎么知道分配多大的空間給這個對象!
看到這里, old.h頭文件可以更新如下了:
// old.h: 這就是你接收的類 // #include <iosfwd> //新替換的頭文件 #include <list> // 5 個 分別是file , db, cx, deduce or error , 水平有限沒有模板類 // 只用 file and cx 有虛函數(shù). #include "file.h" // class file , 作為基類不能刪除,刪除了編譯器就不知道實(shí)例化old 對象時分配多大的空間了 #include "db.h" // class db, 作為基類不能刪除,同上 #include "cx.h" // class cx #include "deduce.h" // class deduce // error 只被用做參數(shù)和返回值類型, 用前向聲明替換#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 是模版類型,既不是函數(shù)參數(shù)類型也不是函數(shù)返回值類型,所以cx.h 頭文件不能刪除 deduce deduce_d_; // deduce 是類型定義,也不刪除他的頭文件 }; inline std::ostream& operator<<( std::ostream& os,const old& old_val ) { return old_val.print(os); }
到目前為止, 刪除了一些代碼, 是不是心情很爽,據(jù)說看一個程序員的水平有多高, 不是看他寫了多少代碼,而是看他少寫了多少代碼。
如果你對C++ 編程有更深一步的興趣, 接下來的文字你還是會看的,再進(jìn)一步刪除代碼, 但是這次要另辟蹊徑了
2. 刪除不必要的一大堆私有成員變量,轉(zhuǎn)而使用 "impl" 方法
2.1.使用 "impl" 實(shí)現(xiàn)方式寫代碼,減少客戶端代碼的編譯依賴
impl 方法簡單點(diǎn)說就是把 類的私有成員變量全部放進(jìn)一個impl 類, 然后把這個類的私有成員變量只保留一個impl* 指針,代碼如下
// file old.h class old { //公有和保護(hù)成員 // public and protected members private: //私有成員, 只要任意一個的頭文件發(fā)生變化或成員個數(shù)增加,減少,所有引用old.h的客戶端必須重新編譯 // private members; whenever these change, // all client code must be recompiled };
改寫成這樣:
// file old.h class old { //公有和保護(hù)成員 // public and protected members private: class oldImpl* pimpl_; // 替換原來的所有私有成員變量為這個impl指針,指針只需要前向聲明就可以編譯通過,這種寫法將前向聲明和定義指針放在了一起, 完全可以。 //當(dāng)然,也可以分開寫 // a pointer to a forward-declared class }; // file old.cpp struct oldImpl { //真正的成員變量隱藏在這里, 隨意變化, 客戶端的代碼都不需要重新編譯 // private members; fully hidden, can be // changed at will without recompiling clients };
不知道你看明白了沒有, 看不明白請隨便寫個類試驗(yàn)下,我就是這么做的,當(dāng)然凡事也都有優(yōu)缺點(diǎn),下面簡單對比下:
使用impl 實(shí)現(xiàn)類 |
不使用impl實(shí)現(xiàn)類 |
|
優(yōu)點(diǎn) |
類型定義與客戶端隔離, 減少#include 的次數(shù),提高編譯速度,庫端的類隨意修改,客戶端不需要重新編譯 |
直接,簡單明了,不需要考慮堆分配,釋放,內(nèi)存泄漏問題 |
缺點(diǎn) |
對于impl的指針必須使用堆分配,堆釋放,時間長了會產(chǎn)生內(nèi)存碎片,最終影響程序運(yùn)行速度, 每次調(diào)用一個成員函數(shù)都要經(jīng)過impl->xxx()的一次轉(zhuǎn)發(fā) |
庫端任意頭文件發(fā)生變化,客戶端都必須重新編譯 |
改為impl實(shí)現(xiàn)后是這樣的:
// 只用 file and cx 有虛函數(shù). #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.cpp class oldimpl{ std::list<cx> cx_list_; deduce dudece_d_; };
3. 刪除不必要的類之間的繼承
面向?qū)ο筇峁┝死^承這種機(jī)制,但是繼承不要濫用, old class 的繼承就屬于濫用之一, class old 繼承file 和 db 類, 繼承file是公有繼承,繼承db 是私有繼承,繼承file 可以理解, 因?yàn)閒ile 中有虛函數(shù), old 要重新定義它, 但是根據(jù)我們的假設(shè), 只有file 和 cx 有虛函數(shù),私有繼承db 怎么解釋?! 那么唯一可能的理由就是:
通過 私有繼承—讓某個類不能當(dāng)作基類去派生其他類,類似Java里final關(guān)鍵字的功能,但是從實(shí)例看,顯然沒有這個用意, 所以這個私有繼承完全不必要, 應(yīng)該改用包含的方式去使用db類提供的功能, 這樣就可以
把"db.h"頭文件刪除, 把db 的實(shí)例也可以放進(jìn)impl類中,最終得到的類是這樣的:
// 只用 file and cx 有虛函數(shù). #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.cpp class oldimpl{ std::list<cx> cx_list_; deduce dudece_d_; };
小結(jié)一下:
這篇文章只是簡單的介紹了減少編譯時間的幾個辦法:
1. 刪除不必要的#include,替代辦法 使用前向聲明 (forward declared )
2. 刪除不必要的一大堆私有成員變量,轉(zhuǎn)而使用 "impl" 方法
3. 刪除不必要的類之間的繼承
這幾條希望對您有所幫助, 如果我哪里講的不夠清楚也可以參考附件,哪里有完整的實(shí)例,也歡迎您發(fā)表評論, 大家一起討論進(jìn)步,哦不,加薪。 呵呵,在下篇文章我將把impl實(shí)現(xiàn)方式再詳細(xì)分析下,期待吧...
以上就是小編為大家?guī)淼臏p少C++代碼編譯時間的簡單方法(必看篇)全部內(nèi)容了,希望大家多多支持億速云~
免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀點(diǎn)不代表本網(wǎng)站立場,如果涉及侵權(quán)請聯(lián)系站長郵箱:is@yisu.com進(jìn)行舉報,并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。