C++ 編程過程中,有哪些常犯的壞習慣,哪怕對於多年經驗的程序員也會出現?
(more)effective c++ 里說的都是好習慣,很少有人全部做到吧,但是有哪些不好的習慣經常出現呢,高人們來說說,讓我等菜鳥少犯錯誤吧
說說我見到的一些不良現象吧。有些程序員幹了十多年還這樣。
1.命名空間。
不用namespace。導致全局空間被污染。組織混亂帶來維護障礙或使用不便。或完全依靠前綴把名字起的老長,在樹後面都能看見。2.代碼一鍋粥。
不使用介面隔離實現/頭文件設計不當。
文件間依賴過度緊密難於分離。或過於鬆散造成使用困難。以及包含次序耦合帶來的編譯問題。3.預編譯頭失調。
要麼完全不用,造成編譯慢;要麼用的太多,在預編譯頭裡「隱藏」了一些本該被include的定義,使功能在庫外部無法使用。4.大清江山萬萬年。
抵觸任何變化。拒絕使用c++11及更新的語言特性。即使編譯器支持也不用auto,不用lambda表達式,不用override……把業務邏輯寫得冗長複雜。5.狂熱者
對c/c++盲目推崇,對其它語言盲目貶低。如用性能差貶低C#和Java;
如認為所有有GC的語言都是解釋執行的/都是弱類型的/都是自動管理資源的。直到被piapia打臉。6.憑直覺優化
不會或不愛使用性能分析手段。很多人你跟他談結構不好,他開口閉口說「為了性能」跟你打嘴炮。然後你一問具體數據就變成了:我覺得會更快……7.愛SM黨
濫用內嵌彙編迷信其的效率和逼格。代價包括開發效率差,無法跨平台,不便閱讀,無法享受編譯器優化……常常只是把簡單問題複雜化,複雜問題天書化。8.暗中破壞。
不寫或濫用防禦代碼。或不檢查指針有效性。由函數依賴傳入參數決定是否崩潰。
或用assert代替,崩潰後連日誌輸出都沒有。發布後出了問題就抓瞎。或有檢查,但失敗後保持沉默,把問題隱患擴散到其他地方。9.異常。
對異常不了解也不想了解。或完全不用導致某些邏輯複雜。或混淆c++異常與操作系統異常。或隨意catch並忽視異常。或沒有對應防禦機製造成內存泄露/漏過初始化等問題。10.華麗的參數表。
不對參數封裝或抽象為對象。使得介面難用/易出錯/無謂的參數copy。
11.不加甄別的繼承c遺產。
如隨意的類型轉換/類型擦除/函數參數默認值/函數變參/濫用union代替轉換函數等等。代碼建立在過多的隱喻上。12.濫用const。
過分強調使用const,近乎原教旨主義。綁架其他的介面也無謂提升複雜度。一點需求變更就引發散彈槍式的重構。13.閉門造車
因懶得了解stl而造一些無聊的輪子。排序、搜索、數組、字元串……都單寫一套。問題多,效率差,還不通用。14.永遠的繼承。
從不組合。一說擴展功能就想到繼承,甚至多重繼承。把類型寫得龐大臃腫。最後發現很難不動介面做任何改動。然後完蛋。15.四通八達。
從不封裝數據成員。並且喜歡直接訪問靜態成員或全局變數。飛線如亂麻。模塊間高度耦合,數據變更失控。一有新需求就傻眼。
16.寫虛函數總是不會錯
濫用虛函數,鼓吹應一切皆虛。直到某天在構造函數里調了一下……17.多愁善感。
從不畫圖,理由是覺得沒必要/能想明白/不會畫/習慣先寫再畫。結果就是業務邏輯混亂,代碼層次模糊,對象生存期說不清楚。隨意持有其他對象指針。總抱怨意料之外的事發生。18.酷愛吃糖
濫用operator/模板/宏來打造語法糖。把簡單代碼寫到編輯器推導不出才滿意。19.CPU:「怪我嘍」
最常見的是把經過運算的float直接和定值比較。出現問題怪CPU不靠譜,把他的數「算壞了」。20.warning是啥?
認為能通過編譯就大功告成了。然後埋一個如分支無返回值之類的雷到運行時。
21.只要能用就好。
分不清平台API/專屬庫與C/C++標準庫。比如用MFC在win下寫伺服器。然後發現在Linux上無法編譯部署。22.懶癌晚期
懶得寫或不會寫測試。從不用代碼測試代碼,拿測試人員或用戶當小白鼠。或把一段簡陋的臨時代碼插到程序某處運行。第一次能跑就把測試刪除了。23.性能一筋
過度具象的盲目追求「性能」。設計時言必提性能。茴字100種寫法全都為性能。
高估函數調用開銷。高估new/delete的開銷。然後使用dowhile處理分枝。……把代碼搞得像(友善度)。24.我最專一。
只用一種技術/語言/平台。一切沒聽過的東西都不存在。25.甩手掌柜。
基礎問題不想了解。如不理解字元編碼與傳輸格式的區別;
如不知道各種調用約定的區別;如不知道結構成員對齊的含義(只有1位元組對齊時代碼才能工作)……其實就是懶得花幾分鐘看看書的事。26.金口玉言。
代碼從不重構。也不許別人動。當你指出某某行缺陷,對方卻說NN年都用過來了,肯定靠譜。27.卡牌:誤導
注釋與代碼對不上,命名與作用都不上,變數名與類型對不上。每次看到如「軍銜」(rank)與「角色等級」(playerLevel)兩個變數相比較時就想抽人。28.神奇的前綴。
當你看到m_lprpglpmcCur時,會想到這事啥?會理解成當前的RPGLocalPlayerMotionController*嗎?這縮寫完全是把人搞昏。另外即使沒有IDE提示,我也認為這東西一點用都沒有。縮小變數作用範圍才是更好的選擇。29.到處都是坑
不了解語言/庫的特性卻亂用。如算符優先順序問題;如表達式取值次序問題;如不同版本vector實現差異問題;如臨時對象做參數傳遞問題;……天天嚷著語言坑,其實(友善度)。30.大魔術師。如把棧上的char[]cast為某類型後,手動調構造/析構。玩憑空造物。如在對象里克隆自己、幹掉自己、再把新對象塞回自己的管理器里。玩金蟬脫殼。……總之就是有話不好好說,多種方式花樣作死。先open, 然後n多處理, 末了close, 自以為天衣無縫。
誰知道「 n多處理」 之中,拋出了一個異常。。。然後他「吃一塹長一智」,到處加上
try ... catch(...),自以為萬無一失!最後他的代碼只能扔掉,由「會的人」重寫。
============== 2014-10-24 補充 =======================
那麼問題來了,到底該怎麼釋放局部資源呢?回答是 Resource Acquisition Is Initialization (RAII)。簡單地說,『清理/釋放』工作, 要放到局部變數的dtor中去做。
例子:class AutoCloseFd // "fd"型資源的自動釋放類{public: AutoCloseFd(int fd) { _the_fd = fd; } ~AutoCloseFd() { close(_the_fd); }int _the_fd;};我們怎麼用它呢?
void do_many_thing()
{int fd = open( 打開一個文件,或者通訊管道。。。 );if (fd &<0 ) {// 沒錯,我們喜歡拋異常,但是一般不抓異常。// 只有在"頂層入口點"或者,抓了之後會有B計劃而不是"報錯,返回",這兩種情況,才會去"try catch"throw OurException( "Failed to open xxx, errnor=%d" ,errno ); }// 現在fd已經成功獲得,我們要確保它一定會關 。這就是RAII:
AutoCloseFd __we_wont_forget_to_close_this_fd (fd);//然後你就不用管這個fd了 do_some_thing_may_throw_ex(fd); // 完全不用擔心 do_some_thing_other_may_throw_ex(fd); // 我有RAII,還怕它throw?}多年C++經驗,簡單羅列一下,權當拋磚引玉:
- 非底層的業務項目中,不使用stl、boost等成熟組件,自己造輪子
- 自己管理內存,不使用智能指針
- 在頭文件中using namespace
- 使用指針前,不判斷空指針
- 數字、指針變數聲明時,不賦初值
- 不使用虛析構函數
- 沒有充分利用RAII(智能指針、析構)機制的前提下,使用異常
我曾經看代碼,有人為了實現Clone操作把operator=聲明為virtual的,這個我覺著好危險。
不了解線程並發,一個真實的案例:
由hashmap管理的對象的指針,取出來加引用計數,放回去減引用計數,減到0就釋放對象,用鎖保護引用計數。
這個保護引用計數的鎖,被放在了每個對象里……在頭文件里using namespace
難道不是學會了鎚子到處找釘子嗎?
最壞的習慣就是,在可以選擇的情況下,選擇為了兼容C語言而被迫存在的那些feature。
分配了內存忘記了釋放
打開了文件忘記了關
吃了飯忘記了洗碗
拉了屎忘記了擦屁股//聲明一個靜態整型變數
static int asdinfadga;
//聲明一個雙精度浮點型變數
double xxx;
總覺得C++可以干一切事情,守舊。
不願意學習其他語言其他工具,這真是戰略上的壞習慣。編程世界中還有另一番天地。至於使用上的壞習慣(戰術層面),一是太多了。二跟新手們講了,其實也沒用,只有自己濫用吃了苦頭,才會吃塹長智。所以說最好的捷徑是沒有捷徑,慢慢來吧,不用操之過急。1:寫C-Style C++代碼2:隨處可見的傳值(你考慮過複製開銷么)3:分配內存之後不管了等著操作系統擦腚(內存泄露)4:一切皆為struct(class的保護被你吃了?所有成員都公開?)5:一堆一堆又一堆的裸指針(當然有時候不得不這麼干,不過少干點更安全)6:胡亂重載operator7:寧願用#define也不用static const8:說了一萬遍的全局using namespace9:寧願用foo_hello()也不用namespace
當標準庫函數isalpha的參數傳入一個比較大的整數後,程序coredump了。。。
1、重複造輪子。2、 }
用裸指針
輕易猜測並相信某個API是某種特定的實現;
不仔細看API說明,特別是注意事項,就根據以往的經驗直接上手;拒絕學習新語言或者帶著舊的視角看新語言
使用兼容C的API和數據結構
昨天幫室友de了個bug,他在搞在搞深度學習,一堆我看不懂的演算法,最後跪在了哪你們猜猜看char vec[12];
sprintf(vec, "%012d", number);
根本問題在哪?
不是vec[12]太少——就算你開了13或者1024,也不能避免有一天sprintf(str, "%1024d",然後掛掉問題在C大部分編譯器允許做的事情,談不上習慣好壞,不同的公司和項目,風格不一樣,水平不同,要求也不一樣。像我自己就會覺得沒有先寫好測試代碼就實現功能很危險,但不見得這麼做就有問題。
習慣問題不好說,但是大膽預測造成問題的原因中,與指針相關的不低於60%
推薦閱讀:
※大學每天用6個小時編程,以後會怎樣?
※stl的sort和手寫快排的運行效率哪個比較高?
※C++輸出hello world,請從電子電路、內存CPU、程序層面解釋一下?
※如何在一個月內提高C++水平?
※如何說服同學在寫C++程序的時候用cstdio而不是stdio.h?