大型C++項目必須注意的幾個小問題


大型C++項目必須注意的幾個小問題

 

有些問題對於小型的C++項目來說可能無關緊要,但對於大中型C++項目來講,這些問題卻成了大問題。什么樣的項目算是小型項目呢,什么樣的算是大中型項目呢,我認為10萬LOC以下為小型項目,10-50萬LOC為中型項目,50萬LOC以上為大型項目。當然,不能單純地以代碼行數作為衡量標准,前幾天產品重構,我用四二三十行代碼換掉了原來的三四千行代碼,那這個項目的規模是用這二三十行來計算呢,還是用那三四千行算呢?軟件很難有一個准確的度量標准,暫以行數作為一種參考性標准吧。

當項目較大量一些在小型項目中不需要考慮的問題也變得非常重要了,對這些問題還是要認真考慮的。

1. 目錄結構
小型項目可能一個目錄就夠了,把所有頭文件和源代碼及相關的工程文件放到一起,但項目較在時目錄中的文件也就多了起來,文件一多查看就很不方便,如果對各種文件執照功能或類別進行一些分組就會方便很多,比如查找某個文件也很迅速。這一點,我覺得boost和chrome做得是很好的,包括它們的命名規則都很統一,而ACE做得就不是太好,所有文件放到一個目錄里,好幾百個文件,如果安裝了CVS或SVN的windows客戶端,在打開資源管理器時還要掃描一下文件是否與版本庫關聯,使得反應其慢,感覺不是太好。

2. 模塊划分
一個大型的項目不可能當成一個整體工程一下子編譯完的,如果有幾百個文件,使用一個Makefile或者VS的工程,每編譯一次都會很耗時,如果需要經常調試的話,編譯就會很消耗時間。如果把整個工程划分成多個模塊,不同模塊以靜態庫或者動態庫的方式進行組織,各模塊不僅可以分別編譯,單獨測試,而且對於多個人協作的並行開發是非常有利的。因為不論是VSS、CVS還要SVN等這些主流的版本管理軟件對於並行開發都不能做到盡善盡美,其實這個也不可能,因為軟件本身不可能識別程序,像人一樣去合並程序代碼,如果幾個人會經常修改同一個模塊的代碼時沖突可能就比較多,解決沖突的次數多了不僅影響工作效率,而且還影響心情。

3. 頭文件層次
為什么把頭文件的層次結構單獨分出來而不是直接把模塊划分放到同一個問題里呢?就是要突出其重要性。如果在划分模塊時沒有把模塊的組織方式設計合理,很有可能導致頭文件循環引用的情況,這樣程序就無法編譯。

有一些特殊情況就更需要注意,比如windows中的winsock2.h和windows.h存在沖突,如果先引用windows.h再引用winsock2.h就會出現重定義的情況。如果想使用Win32 Socket API 2,則必須先引用winsock2.h然后引用windows.h,可是如果在多個文件里引用windows,如果順序控制不好就有可能導致混亂,從而程序無法編譯。個人認為最好的方法就是把該引用放入一個頭文件,比如叫sysinc.h,然后所有編譯單元的頭文件都在第一行引用sysinc.h,然后所有的.cpp文件在第一行引用對應的頭文件,這樣即可保證winsock2.h和windows.h的引用順序。

4. 命名空間
C語言是不支持命名空間的,以前人們為了避免標識符沖突都是加前綴或者后綴,但這樣會使得標識符很長,看起來不舒服,而且寫起來很麻煩(雖然現在IDE對智能感知的支持已經使這個變得很方便),還老感覺怪怪的,不夠自然。所以,后來的語言都對命名空間(或類似功能)進行了支持,如C++、C#、Java、Python等,所以在大型的C++項目為什么不用這個新的特性呢?

使用不同命名空間中的標識符識不要圖省事使用using namespace ns; 這樣的語句把整個ns命令空間內的標識符都引進來,這樣便失去了命名空間的作用。因為這樣做可行是需要一個前提的,就是知道這個不會沖突,可是誰能預知未來不會沖突呢?前些天就遇到在WINDOWS、Linux、AIX三個平台編譯都沒有問題,可是到了Solaris就出了問題,原因是在系統的頭文件if.h中有一個結構定義struct map,這個跟C++標准庫中的模塊map沖突,導致無法編譯。

在這方面,boost的作者們不愧是C++的專家,這方面做得很好,而ACE就不是太好,所有標識符通過前綴ACE_來標識,保必呢,而且是大寫,看起來很不舒服,如果要用呢只能忍了,或者自己單獨寫一個頭文件,使用typedef或者用宏定義把標識符都給改了,然后把新定義的標識符封裝到一個ace命令空間中,這也是個不錯的主意。

5. 代碼注釋
時間長了誰也難保證記住每一段代碼的用途,特別是一些特別技巧,一定要隨手寫下來,為以后自己維護或與同事合作都是很必要的,不要怕別人看懂自己的代碼對自己有威脅,開放一些,只有互相學習才能促進發展,你把代碼給了他人,他人也會給你建議幫你進步的,中國前一百年的歷史就是固步自封的沉痛教訓,國家如此,個人亦如此。

6.版本管理工具
如果幾個人同時維護幾十萬行代碼都沒有使用版本管理工具的話,那你每天肯定只能有一種感覺,那就是在浪費生命。浪費自己的生命也就算了,如果作為一個領導者不對此做點改變的話,那就是浪費別人的生命,也就是謀財害命。

7. 代碼規范
每個人可能都有自己的編碼風格,但是同一個項目,應該只有一種風格,否則可能會影響工作,雖然程序本身的性能和功能都沒有會有問題,但對於產品的實際開發過程會有影響,因為它會影響到一些有想法的工程師的心情,當然就會影響工作效率,因為他們老感覺那些代碼不夠規范,總想去修改這些細節,當改過后可能會被另外的工程師改成另一種風格。所以必須有統一的風格,以統一團隊每一位成員的行為。

有一個細節,我還是有體會的。項目中一些舊的代碼寫得很亂,有的一個函數都能寫幾百行,而且代碼塊的花括號{ 和}都是另起一行if-else都是在單獨的行,這樣着滿屏都是花括號和if - else,看起來代碼很不緊湊,比如:
if( cond 1 )
{
line …
line …
}
else
{
line …
}
改成
if( cond 1 ){
line …
line …
}else{
line …
}

看起來就緊湊多了。
還有一些代碼層次很深,一塊代碼可能都有四層if條件嵌套,看起來是很費勁的,如果在每一次條件判斷都進行退出的話,可讀性將會有很大的改善,如
if( cond 1 ){
if( cond 2 ){
if( cond 3){
work line
}
}
}
如果改成下面代碼,看起來會更舒服些
if( ! cond 1)
return –1;
if ( ! cond 2 )
return –2;
if( !cond 3 )
return –3;
work line

當然寫成
if( cond 1 && cond 2 && cond 3 ){
work line
}
更好,但有些時候並沒有這么方便。

8. 自動化構建
如果一個大型項目包含幾十個小模塊,每次都要手工編譯依賴項的話,工作效率是低下的,如果能夠自動構建必要的模塊將可以大大提高工作效率,即使每次能少輸入幾個字符也是必要的。
比如我們的一個項目需要在windows、Linux、AIX、HPUX、Solaris五個平台上運行,如果每次編譯前都執行configure,然后再執行make,也不是很方便,如果公共的部分寫到一個Makefile.comm中,然后為每一個平台的平台依賴項分別寫一個Makfile.win,Makefile.linux, Makefile.aix,Makefile.hpux, Makefile.solaris,這樣每次只需要輸入make –f Makefile.os來編譯,但這樣要輸入的要挺多的,可以再建一個Makefile文件,然后自動檢測系統平台,然后調用對應的Makefile,以后只需要輸入make就行了,再想簡單,可以寫個小腳本,文件名為m,里面調用make指令,這樣每次只需要輸入./m即可,如果還嫌輸入的字符太多的話,可以把.加到你的PATH變量中,以后直接輸入m就可以了。如果再想簡單呢?那只能使用特異功能,玩兒心靈感應了,我實在是想不到其它的辦法了。
只是個例子而已,可能實際開發過程中需要更多其它的工作,但當你發現你做某件事時不再是第一次,你就可以考慮下次遇到后如何簡化了,這也是“程序修煉之道”中提到的DRY(Don’t Repeat Yourself)法則。

9. 自動化測試
對於一個大型項目,每次構建后都進行手動測試的工作量是很大的。如果公司沒有研發團隊配備測試人員,而且測試部不能給及時測試的話,研發力會將會有大量的人力浪費在測試上,所以構建自己的自動化測試系統是一項至關重要的內容。

暫時談到這兒吧,想到再記下來,有機會與網友探討,各位晚安。

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM