閱讀838 返回首頁    go 阿裏雲 go 技術社區[雲棲]


C++編譯時間過長解決方案

對於一個中型或者以上項目,編譯時間本來就不短,如果在編碼過程中,一些問題不注意,將使編譯時間更長,下麵介紹幾點需要注意的地方。

 

關於C++ coding Standards》以下幾條整改原則:


關於include的原則最多,因為包含頭文件相當於將代碼複製到本文件來編譯,而頭文件又經常是用來被別人包含的,所以工程文件多了,每個文件都有include鏈(包含的文件又include了其他文件),該鏈條不會止步 於你工程,而會延伸到你所有使用的第3方庫裏麵。

能夠去掉的include就去掉

說明:1.代碼編寫過程中或多或少都有一些曆史遺留的不必要的頭文件包含在你的文件裏麵,找到他們並去掉之。

2.去掉include鏈裏麵重複的include

能夠在cpp裏麵include的頭文件不要在頭文件裏麵include
      
說明:盡量去掉每個cpp會被串起來的頭文件膨脹的機會。

 

把大多數模塊都要使用的庫文件或者穩定類的頭文件include放到預編譯頭文件“stdafx.h”裏麵
說明:由於預編譯頭文件裏麵include的內容隻會compile一次而被link多次,把一些常用類放到這裏會降低很多編譯時間,但也不能亂來,要點在 大多數穩定,如果一個頭文件經常變化,他的一次小改動都會引起整個工程rebuild,哪怕隻是一個注釋,因為所有的cpp文件都包含了 stdafx.hstdafx.h又包含了這個容易變動的頭文件。

 

想要具體了解可以參考這本書,這本書C++ coding Standards》(英文版)現在在我這裏,另外可以參考《fective C++》關於編譯的章節,這本書在鄒鳴哪裏。

 

    另外可以在繼承關係,如果使用不當,也會產生編譯時間過長的問題,下麵這個帖子轉自C++ FAQs關於《我的程序為什麼編譯時間過長》,講解者是Bjarne Stroustrup

 

Bjarne Stroustrup博士,1950年出生於丹麥,先後畢業於丹麥阿魯斯大學和英國劍撟大學,AT&T大規模程序設計研究部門負責人, AT&T 貝爾實驗室和ACM成員。1979年,B. S開始開發一種語言,當時稱為"C with Class",後來演化為C++1998年,ANSI/ISO C++標準建立,同年,B. S推出其經典著作The C++ Programming Language的第三版。

你的編譯器可能有問題。也許它太老了,也許你安裝它的時候出了錯,也許你用的計算機已經是個古董。在諸如此類的問題上,我無法幫助你。

但是,這也是很可能的:你要編譯的程序設計得非常糟糕,以至於編譯器不得不檢查數以百計的頭文件和數萬行代碼。理論上來說,這是可以避免的。如果這是你 購買的庫的設計問題,你對它無計可施(除了換一個更好的庫),但你可以將你自己的代碼組織得更好一些,以求得將修改代碼後的重新編譯工作降到最少。這樣的 設計會更好,更有可維護性,因為它們展示了更好的概念上的分離。

看看這個典型的麵向對象的程序例子:

class Shape {
public: //
使用Shapes的用戶的接口
virtual void draw() const;
virtual void rotate(int degrees);
// ...
protected: // common data (for implementers of Shapes)
Point center;
Color col;
// ...
};

class Circle : public Shape {
public:
void draw() const;
void rotate(int) { }
// ...
protected:
int radius;
// ...
};

class Triangle : public Shape {
public:
void draw() const;
void rotate(int);
// ...
protected:
Point a, b, c;
// ...
};


設計思想是,用戶通過Shapepublic接口來操縱它們,而派生類(例如CircleTriangle)的實現部分則共享由protected成員表現的那部分實現(implementation)。

這不是一件容易的事情:確定哪些實現部分是對所有的派生類都有用的,並將之共享出來。因此,與public接口相比,protected成員往往要做多 得多的改動。舉例來說,雖然理論上中心”(center)對所有的圖形都是一個有效的概念,但當你要維護一個三角形的中心的時候,是一件非常麻煩的 事情——對於三角形,當且僅當它確實被需要的時候,計算這個中心才是有意義的。

protected
成員很可能要依賴於實現部分的細 節,而Shape的用戶(譯注:user此處譯為用戶,指使用Shape類的代碼,下同)卻不見得必須依賴它們。舉例來說,很多(大多數?)使用 Shape的代碼在邏輯上是與顏色無關的,但是由於Shape顏色這個定義的存在,卻可能需要一堆複雜的頭文件,來結合操作係統的顏色概念。

protected部分發生了改變時,使用Shape的代碼必須重新編譯——即使隻有派生類的實現部分才能夠訪問protected成員。

於是,基類中的實現相關的信息”(information helpful to implementers)對用戶來說變成了象接口一樣敏感的東西,它的存在導致了實現部分的不穩定,用戶代碼的無謂的重編譯(當實現部分發生改變時), 以及將頭文件無節製地包含進用戶代碼中(因為實現相關的信息需要它們)。有時這被稱為脆弱的基類問題”(brittle base class problem)

一個很明顯的解決方案就是,忽略基類中那些象接口一樣被使用的實現相關的信息。換句話說,使用接口,純粹的接口。也就是說,用抽象基類的方式來表示接口:

class Shape {
public: //
使用Shapes的用戶的接口
virtual void draw() const = 0;
virtual void rotate(int degrees) = 0;
virtual Point center() const = 0;
// ...
//
沒有數據
};

class Circle : public Shape {
public:
void draw() const;
void rotate(int) { }
Point center() const { return center; }
// ...
protected:
Point cent;
Color col;
int radius;
// ...
};

class Triangle : public Shape {
public:
void draw() const;
void rotate(int);
Point center() const;
// ...
protected:
Color col;
Point a, b, c;
// ...
};


現在,用戶代碼與派生類的實現部分的變化之間的關係被隔離了。我曾經見過這種技術使得編譯的時間減少了幾個數量級。

但是,如果確實存在著對所有派生類(或僅僅對某些派生類)都有用的公共信息時怎麼辦呢?可以簡單把這些信息封裝成類,然後從它派生出實現部分的類:

class Shape {
public: //
使用Shapes的用戶的接口
virtual void draw() const = 0;
virtual void rotate(int degrees) = 0;
virtual Point center() const = 0;
// ...
// no data
};

struct Common {
Color col;
// ...
};

class Circle : public Shape, protected Common {
public:
void draw() const;
void rotate(int) { }
Point center() const { return center; }
// ...
protected:
Point cent;
int radius;
};

class Triangle : public Shape, protected Common {
public:
void draw() const;
void rotate(int);
Point center() const;
// ...
protected:
Point a, b, c;
};

 

 

最後更新:2017-04-02 00:06:15

  上一篇:go SNMP(Simple Network Management Protocol)簡單網絡管理協議
  下一篇:go STL學習筆記一(深入VC之STL係列)