c++編譯模型


C++編譯特點

  • 與C兼容

不僅是語法兼容,更重要的是兼容C語言的編譯模型與運行模型,也就是能直接使用 C語言的頭文件。

  • C++語言的三大約束

與C兼容,零開銷,值語義。


 

單遍編譯,C++繼承了C的單遍編譯,但是影響了名字查找和函數重載決議。從頭到尾掃描一遍源碼,一邊解析源碼,一邊生成目標代碼,也就是編譯時,只能看到目前已經解析過的源碼,看不到後面的源碼,也是C語言中需要函數聲明的原因。結構體必須先定義。 

重載,爲了實現函數重載,c++編譯器採用名字改編的辦法,爲每個重載函數生成獨一無二的名字,這樣鏈接的時候就能找到正確的函數。返回類型不參與函數重載。函數重載決議,當C++編譯器讀到一個函數調用時,它必須從已經看到的同名函數中選出最佳的函數,哪怕後面出現了更合適的匹配。

C++ 只能通過解析源碼來了解名字的含義。意味着要準確理解一行代碼的意義,需要通讀之前的所有代碼。AA BB(CC);這句話既可以聲明函數,也可以定義變量,代碼的行爲跟AA BB的完整定義有關。

 

編譯過程

編譯的步驟

preprocessor    /complier    /assembler    /linker

1.      每個源文件作爲一個編譯單元,可能會包含上百甚至上千個頭文件。每一個編譯單元,這些頭文件都會被從硬盤讀進來一遍,然後被解析一遍。

2.      每個編譯單元都會產生一個obj文件,然後所以這些obj文件會被link到一起。

 

一個C++可執行文件是debug build 還是 release build?通常的做法是判斷class template 的短函數成員有沒有被inline展開。

g++ -Wall *.cc 沒有優化的bulid,不會inline展開。debugbuild.

g++ -Wall -o2 *.cc inline展開。releasebuild. 編譯器自動生成的析構函數也是inline的。

 

編譯的結果是靜態庫,動態庫,或程序。

1、  一致的內存管理,Linux共享庫與應用程序共享一個heap,因此動態庫分配的內存可以交給應用程序去釋放。

2、  一致的初始化,動態庫裏的靜態對象的初始化和程序其他地方的靜態對象一樣。

3、  在動態庫中可以放心使用class, STL。

 

由於C++ 的頭文件與源文件分離,並且目標文件裏沒有足夠的元數據供編譯器使用,因此必須同時提供庫文件和頭文件。也就是說要想使用一個已經編譯好的C/C++ 庫(無論是靜態庫還是動態庫),我們需要兩樣東西,一是頭文件(.h),二是庫文件(.a 或.so),這就存在了兩樣東西不匹配的可能。

如果替換了某個程序用到的動態庫,先前運行正常的這個程序使用的將不再是當初build 和測試時的代碼。結果是程序的行爲變得不可預期。

一旦動態庫或靜態庫可能頻繁更新,爲了避免頭文件和庫文件不一致,建議直接使用庫源代碼。這樣徹底避免頭文件與庫文件之間的時間差,也不用爲庫的版本搭配操心。這麼做的缺點是編譯時間很長。

 

編譯加速

1.        在頭文件中使用前置聲明,而不是直接包含頭文件。

很多時候前置聲明某個namespace中的類會比較痛苦,而直接include會方便很多,千萬要抵制住這種誘惑;類的成員,函數參數等也儘量用引用,指針,爲前置聲明創造條件。

2.        高度模塊化

不要把兩個不相關的類,或者沒什麼聯繫的宏定義放到一個頭文件裏。內容要儘量單一,從而不會使包含他們的文件包含了不需要的內容。代碼中最"hot"的那些頭文件找出來,然後分成多個獨立的小文件,效果相當可觀。

3.        特別注意inline和template

這是C++中兩種比較"先進"的機制,但是它們卻又強制我們在頭文件中包含實現。

4.        不要有太多的AdditionalInclude Directories

編譯器定位你include的頭文件,是根據你提供的include directories進行搜索的。

5.        並行編譯和更好的磁盤

CPU有8核的,每次一build,就是8個文件並行着編。編譯速度慢很大一部分原因是磁盤操作。make的-j參數可以使make進行並行編譯。



 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章