我的女朋友漏電了–論C++中的失敗(failure),缺陷(bug)和異常(exception)
我承認有點標題黨了,不過這真的是一篇寫軟件的文章,所以如果你已經抽出了一張面巾紙,那麽趁早再把它完美的放回去。這篇軟件文章很軟,源代碼不多,而且大部分都是偽代碼。所以很適合所有人看。我特別推薦年輕的初學者,把紙巾放回去後,繼續看下去。如果把這幾個概念理清楚,對未來的工作非常有幫助。
先說失敗(failure)。常見的軟件的失敗主要分為三種,編譯失敗,運行失敗,結果失敗。下面通過一個程序的例子來說明:
你可以把編譯失敗理解成世界上最聰明的一堆程序員在幫你審核你的代碼。如果審核失敗,他們會提醒你在造成嚴重錯誤之前修改你的問題。單從這一點看,C++也比一些解釋性的語言更加安全。例如源碼中你想把變量l1修改一下。但是由於筆誤寫成了ll。C++的編譯器會對你大吼的。但是Python一聲也不吭,看你運行時候的笑話。
運行失敗是指你已經有了可執行程序,當你運行它的時候,它crash了,掛了,死了,kick the bucket了。一個你最經常看見的輸出就是訪問越界內存帶來的Segmentation Fault。所以請記住:男人頭,女人腰,還有越界內存。這三樣東西永遠不要摸,否則會掛掉。
比起結果失敗,你會發現運行失敗是多麽美好的一件事。上面的if語句是C++中非常著名的一個結果失敗的例子。你的程序編譯正常,運行正常,就是結果不對。而且你還不知道原因所在。另外一個常見的原因就是C++的數值計算溢出。這在C++中是非常著名的“未定義”。 C++標準委員會樂觀的認為如果我“未定義”,那麽天才的編譯器程序員會發明一種最聰明的方法來解決這個問題。但是實際的情況是:天才的編譯器程序員是最懶惰的程序員!如果你“未定義”,那麽他們就什麽也不幹。結果就是到底發生什麽,鬼知道!!!
如果你還沒看懂,沒關系!我再給你舉了生活的例子。一個程序員根本就沒有女朋友。這個就類似於編譯時失敗。一個程序員有個女朋友,但是他第一天就去摸人家女孩子的腰。有些地方還真是和尚摸得,你卻摸不得,結果女朋友憤然離去,這可以對應運行時錯誤。最後一種情況當然就是結果錯誤了。程序員有了一個漂亮的女朋友,第一天他去摸人家的腰,女孩子不僅沒有生氣,還嬌羞的把頭靠在程序員的肩旁上,對他說:“討厭了,你剛才把人家摸懷孕了啦!”
現在的問題就是:如何能盡早的發現女朋友是否懷孕?不對,我說錯了,我是想說如何能夠更早的發現失敗?
首先,常見的失敗的原因主要有兩種,一種是缺陷(bug),另外一種是異常(exception, error)。好多人搞不清楚,所以我上段代碼:
請註意,我說的缺陷就是bug的含義。也就是常說的蟲子。缺陷主要有兩種,設計缺陷和實現缺陷。上面的代碼中,用名字來做為查找鍵值就是一種明顯的設計缺陷。如果你在辦公室喊一聲“狗蛋”,會有好多同事答應的。另外一種實現缺陷我們上面介紹過,key==“Yan”寫成了“key=Yan”。這些都會造成程序的失敗。
那麽我們如何能盡早的發現bug呢?一個很牛逼的工具就是unit test。不過C++也提供另外一個有力的工具,那就是assert。Assert用法是最簡單的。下面我們看看它是如何發現我們的缺陷的。
Assert的基本理念可以應對成一句話:“反常必有妖”。上面這段代碼的含義是如果發現Yan賺的錢超過了20萬,那程序一定是哪裏出現問題了。就像你發現你的名字出現在福布斯富豪榜上了,那富豪榜一定是印錯了。你最好復查一下計算薪水的代碼。如果你再閱讀一下上面計算salary的代碼,你就會發現是重名的設計缺陷和if的實現缺陷才讓Yan的salary那麽多。
關於assert,有三點需要說明,第一就是在哪裏放assert和用assert驗證什麽? 一個基本的原則就是函數的各種輸入和輸出值,但是具體上則完全是根據具體的問題和程序員的經驗。你可以把assert想象成傳感器。要檢驗一輛車的質量,一個有經驗的工程師知道哪裏需要放傳感器,用傳感器檢測什麽。而工程師的女朋友會發現:這個顏色我不喜歡!
關於assert 的第二點就是在程序發布版中,assert是失效的。這個很好理解。一個車出廠前才需要用各種傳感器來發現各種缺陷,一但發現缺陷,就應該找到缺陷的原因並修正它,直到零缺陷,你才應該把車出廠去買才對。如果用戶去買車,發現車上都是傳感器。這會影響用戶的速度和體驗;同時就算是用戶發現車漏油這個問題,你讓普通用戶做什麽呢?所以說assert只是工程師用於產品出廠前檢測缺陷的,而不是用於最終產品的。當然,零缺陷只是傳說,所以車廠有召回,軟件公司有補丁。
Assert的第三點是現在c++支持static _assert了。它可以在編譯的時候就發現問題,這印證了我前面說過的,編譯失敗要好於運行失敗。
現在我們回來看看計算薪水的代碼,看看OpenFile這一段,這裏並不是缺陷,而是會發生異常。文件打不開有太多原因,權限不夠,名字不對,別人給刪掉了。所有這些原因都不是我們的程序能完全控制的。雖然這造成了程序的失敗,但是這既不是設計缺陷,也不是實現缺陷,而是一種異常。
對於異常,我們應該用try catch捕捉到這種異常,然後根據上下文做出正確的反應。常見的異常多發生在IO,網絡鏈接和用戶交互上。它和assert一樣,如果要用好很大程度上取決於具體的問題和程序員的經驗。與assert不同的是,它也必須存在於產品的發布版本中。在車的例子中, 車漏油是缺陷,但是車胎紮了就是一種異常。在平時使用的時候,你也應該時刻準備著捕捉到這種異常,並做出正確的反應。這是它和缺陷最大的不一樣的地方。
沒看懂嗎?沒關系。讓我們繼續程序員和女朋友的故事吧。我們上文提到的程序員自從把女朋友摸懷孕後有點怕怕了。他決定從日本買一個型號為“蒼井-瑪麗亞”的女朋友。拿到貨後很快發現女朋友不僅漏氣,而且漏電!漏氣這個事,明顯屬於異常範圍的。因為即使正常的使用,漏氣也會正常地發生。所以一般廠家會提供一些膠水來應對這種情況。
但是漏電這明顯是一個bug,廠家是應該用傳感器在出廠前就發現並修正這一問題的。也就是說廠家是不應該把這一產品出廠來買的。你需要向廠家報告這一缺陷(bug)。正常的情況下,你會收到廠家的教科書式的回復:“It’s not a bug, It’s a feature!”。我還真的承認。女朋友漏電這個事,人家說是feature(特性),Make sense(沒毛病)!
異常是一個很大的topic,感興趣的可以查看我的兩本書《C語言點滴》和《Drop of knowledge of C++》 。裏面有很多的關於異常的介紹。
從程序員和女朋友的故事中,我希望你能記住以下幾點:
1)編譯失敗好於運行失敗,運行失敗好於結果失敗。
2)失敗通常由缺陷和異常造成。
3)對於缺陷(bug),用assert和單元測試在開發期間盡早發現並修正。
4)對於異常(exception),用try catch 在產品使用的全程捕捉並處理。
5)程序員就不應該找女朋友!
本文摘自異步社區,發表人:zhaoyan,作品《我的女朋友漏電了–論C++中的失敗(failure),缺陷(bug)和異常(exception)》未經授權,禁止轉載。
推薦閱讀
2018年5月新書書單(文末福利)
2018年4月新書書單
異步圖書最全Python書單
一份程序員必備的算法書單
第一本Python神經網絡編程圖書
每天與你分享IT好文。
在“異步圖書”後臺回復“關註”,即可免費獲得2000門在線視頻課程
點擊閱讀原文,查看更多內容
閱讀原文
我的女朋友漏電了–論C++中的失敗(failure),缺陷(bug)和異常(exception)