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


高質量C++/C編程指南

 
 
 
 
 
 
 
高質量C++/C編程指南
 
 
 
 
文件狀態
[ ] 草稿文件
[√] 正式文件
[ ] 更改正式文件
文件標識:
 
當前版本:
1.0
    者:
林銳 博士
完成日期:
2001年7月24日
 
 
 
版本/狀態
作者
參與者
起止日期
備注
V 0.9
草稿文件
林銳
 
 
2001-7-1至
2001-7-18
林銳起草
V 1.0
正式文件
林銳
 
 
2001-7-18至
2001-7-24
朱洪海審查V 0.9,
林銳修正草稿中的錯誤
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
前 言... 6
第1章 文件結構... 11
1.1 版權和版本的聲明... 11
1.2 頭文件的結構... 12
1.3 定義文件的結構... 13
1.4 頭文件的作用... 13
1.5 目錄結構... 14
第2章 程序的版式... 15
2.1 空行... 15
2.2 代碼行... 16
2.3 代碼行內的空格... 17
2.4 對齊... 18
2.5 長行拆分... 19
2.6 修飾符的位置... 19
2.7 注釋... 20
2.8 類的版式... 21
第3章 命名規則... 22
3.1 共性規則... 22
3.2 簡單的Windows應用程序命名規則... 23
3.3 簡單的Unix應用程序命名規則... 25
第4章 表達式和基本語句... 26
4.1 運算符的優先級... 26
4.2 複合表達式... 27
4.3 if 語句... 27
4.4 循環語句的效率... 29
4.5 for 語句的循環控製變量... 30
4.6 switch語句... 30
4.7 goto語句... 31
第5章 常量... 33
5.1 為什麼需要常量... 33
5.2 const 與 #define的比較... 33
5.3 常量定義規則... 33
5.4 類中的常量... 34
第6章 函數設計... 36
6.1 參數的規則... 36
6.2 返回值的規則... 37
6.3 函數內部實現的規則... 39
6.4 其它建議... 40
6.5 使用斷言... 41
6.6 引用與指針的比較... 42
第7章 內存管理... 44
7.1內存分配方式... 44
7.2常見的內存錯誤及其對策... 44
7.3指針與數組的對比... 45
7.4指針參數是如何傳遞內存的?... 47
7.5 free和delete把指針怎麼啦?... 50
7.6 動態內存會被自動釋放嗎?... 50
7.7 杜絕“野指針”... 51
7.8 有了malloc/free為什麼還要new/delete ?... 52
7.9 內存耗盡怎麼辦?... 53
7.10 malloc/free 的使用要點... 54
7.11 new/delete 的使用要點... 55
7.12 一些心得體會... 56
第8章 C++函數的高級特性... 57
8.1 函數重載的概念... 57
8.2 成員函數的重載、覆蓋與隱藏... 60
8.3 參數的缺省值... 63
8.4 運算符重載... 64
8.5 函數內聯... 65
8.6 一些心得體會... 68
第9章 類的構造函數、析構函數與賦值函數... 69
9.1 構造函數與析構函數的起源... 69
9.2 構造函數的初始化表... 70
9.3 構造和析構的次序... 72
9.4 示例:類String的構造函數與析構函數... 72
9.5 不要輕視拷貝構造函數與賦值函數... 73
9.6 示例:類String的拷貝構造函數與賦值函數... 73
9.7 偷懶的辦法處理拷貝構造函數與賦值函數... 75
9.8 如何在派生類中實現類的基本函數... 75
9.9 一些心得體會... 77
第10章 類的繼承與組合... 78
10.1 繼承... 78
10.2 組合... 80
第11章 其它編程經驗... 82
11.1 使用const提高函數的健壯性... 82
11.2 提高程序的效率... 84
11.3 一些有益的建議... 85
參考文獻... 87
附錄A :C++/C代碼審查表... 88
附錄B :C++/C試題... 93
附錄C :C++/C試題的答案與評分標準... 97
      
軟件質量是被大多數程序員掛在嘴上而不是放在心上的東西!
    除了完全外行和真正的編程高手外,初讀本書,你最先的感受將是驚慌:“哇!我以前捏造的C++/C程序怎麼會有那麼多的毛病?”
    別難過,作者隻不過比你早幾年、多幾次驚慌而已。
    請花一兩個小時認真閱讀這本百頁經書,你將會獲益匪淺,這是前麵N-1個讀者的建議。
 
一、編程老手與高手的誤區
 
自從計算機問世以來,程序設計就成了令人羨慕的職業,程序員在受人寵愛之後容易發展成為毛病特多卻常能自我臭美的群體。
如今在Internet上流傳的“真正”的程序員據說是這樣的:
(1)    真正的程序員沒有進度表,隻有討好領導的馬屁精才有進度表,真正的程序員會讓領導提心吊膽。
(2)    真正的程序員不寫使用說明書,用戶應當自己去猜想程序的功能。
(3)    真正的程序員幾乎不寫代碼的注釋,如果注釋很難寫,它理所當然也很難讀。
(4)    真正的程序員不畫流程圖,原始人和文盲才會幹這事。
(5)    真正的程序員不看參考手冊,新手和膽小鬼才會看。
(6)    真正的程序員不寫文檔也不需要文檔,隻有看不懂程序的笨蛋才用文檔。
(7)    真正的程序員認為自己比用戶更明白用戶需要什麼。
(8)    真正的程序員不接受團隊開發的理念,除非他自己是頭頭。
(9)    真正的程序員的程序不會在第一次就正確運行,但是他們願意守著機器進行若幹個30小時的調試改錯。
(10)真正的程序員不會在上午9:00到下午5:00之間工作,如果你看到他在上午9:00工作,這表明他從昨晚一直幹到現在。
……
具備上述特征越多,越顯得水平高,資格老。所以別奇怪,程序員的很多缺點竟然可以被當作優點來欣賞。就象在武俠小說中,那些獨來獨往、不受約束且帶點邪氣的高手最令人崇拜。我曾經也這樣信奉,並且希望自己成為那樣的“真正”的程序員,結果沒有得到好下場。
 
我從讀大學到博士畢業十年來一直勤奮好學,累計編寫了數十萬行C++/C代碼。有這樣的苦勞和疲勞,我應該稱得上是編程老手了吧?
我開發的軟件都與科研相關(集成電路CAD和3D圖形學領域),動輒數萬行程序,技術複雜,難度頗高。這些軟件頻頻獲獎,有一個軟件獲得首屆中國大學生電腦大賽軟件展示一等獎。在1995年開發的一套圖形軟件庫到2000年還有人買。羅列出這些“業績”,可以說明我算得上是編程高手了吧?
可惜這種個人感覺不等於事實。
 
讀博期間我曾用一年時間開發了一個近10萬行C++代碼的3D圖形軟件產品,我內心得意表麵謙虛地向一位真正的軟件高手請教。他雖然從未涉足過3D圖形領域,卻在幾十分鍾內指出該軟件多處重大設計錯誤。讓人感覺那套軟件是用紙煳的華麗衣服,扯一下掉一塊,戳一下破個洞。我目瞪口呆地意識到這套軟件毫無實用價值,一年的心血白化了,並且害死了自己的軟件公司。
 
人的頓悟通常發生在最心痛的時刻,在沮喪和心痛之後,我作了深刻反省,“麵壁”半年,重新溫習軟件設計的基礎知識。補修“內功”之後,又覺得腰板硬了起來。博士畢業前半年,我曾到微軟中國研究院找工作,接受微軟公司一位資深軟件工程師的麵試。他讓我寫函數strcpy的代碼。
太容易了吧?
錯!
這麼一個小不點的函數,他從三個方麵考查:
(1)編程風格;
(2)出錯處理;
(3)算法複雜度分析(用於提高性能)。
在大學裏從來沒有人如此嚴格地考查過我的程序。我化了半個小時,修改了數次,他還不盡滿意,讓我回家好好琢磨。我精神抖擻地進“考場”,大汗淋漓地出“考場”。這“高手”當得也太窩囊了。我又好好地反省了一次。
 
我把反省後的心得體會寫成文章放在網上傳閱,引起了不少軟件開發人員的共鳴。我因此有幸和國產大型IT企業如華為、上海貝爾、中興等公司的同誌們廣泛交流。大家認為提高質量與生產率是軟件工程要解決的核心問題。高質量程序設計是非常重要的環節,畢竟軟件是靠編程來實現的。
我們心目中的老手們和高手們能否編寫出高質量的程序來?
不見得都能!
 
就我的經曆與閱曆來看,國內大學的計算機教育壓根就沒有灌輸高質量程序設計的觀念,教師們和學生們也很少自覺關心軟件的質量。勤奮好學的程序員長期在低質量的程序堆中滾爬,吃盡苦頭之後才有一些心得體會,長進極慢,我就是一例。
現在國內IT企業擁有學士、碩士、博士文憑的軟件開發人員比比皆是,但他們在接受大學教育時就“先天不足”,豈能一到企業就突然實現質的飛躍。試問有多少軟件開發人員對正確性、健壯性、可靠性、效率、易用性、可讀性(可理解性)、可擴展性、可複用性、兼容性、可移植性等質量屬性了如指掌?並且能在實踐中運用自如?。“高質量”可不是幹活小心點就能實現的!
 
我們有充分的理由疑慮:
(1)編程老手可能會長期用隱含錯誤的方式編程(習慣成自然),發現毛病後都不願相信那是真的!
(2)編程高手可以在某一領域寫出極有水平的代碼,但未必能從全局把握軟件質量的方方麵麵。
 
       事實證明如此。我到上海貝爾工作一年來,陸續麵試或測試過近百名“新”“老”程序員的編程技能,質量合格率大約是10%。很少有人能夠寫出完全符合質量要求的if語句,很多程序員對指針、內存管理一知半解,……。
領導們不敢相信這是真的。我做過現場試驗:有一次部門新進14名碩士生,在開歡迎會之前對他們進行“C++/C編程技能”摸底考試。我問大家試題難不難?所有的人都回答不難。結果沒有一個人及格,有半數人得零分。競爭對手公司的朋友們也做過試驗,同樣一敗塗地。
 
真的不是我“心狠手辣”或者要求過高,而是很多軟件開發人員對自己的要求不夠高。
要知道華為、上海貝爾、中興等公司的員工素質在國內IT企業中是比較前列的,倘若他們的編程質量都如此差的話,我們怎麼敢期望中小公司拿出高質量的軟件呢?連程序都編不好,還談什麼振興民族軟件產業,豈不胡扯。
 
我打算定義編程老手和編程高手,請您別見笑。
定義1:能長期穩定地編寫出高質量程序的程序員稱為編程老手。
定義2:能長期穩定地編寫出高難度、高質量程序的程序員稱為編程高手。
根據上述定義,馬上得到第一推論:我既不是高手也算不上是老手。
 
在寫此書前,我閱讀了不少程序設計方麵的英文著作,越看越羞慚。因為發現自己連編程基本技能都未能全麵掌握,頂多算是二流水平,還好意思談什麼老手和高手。希望和我一樣在國內土生土長的程序員朋友們能夠做到:
(1)知錯就改;
(2)經常溫故而知新;
(3)堅持學習,天天向上。
 
二、本書導讀
 
    首先請做附錄B的C++/C試題(不要看答案),考查自己的編程質量究竟如何。然後參照答案嚴格打分。
1)如果你隻得了幾十分,請不要聲張,也不要太難過。編程質量差往往是由於不良習慣造成的,與人的智力、能力沒有多大關係,還是有藥可救的。成績越差,可以進步的空間就越大,中國不就是在落後中趕超發達資本主義國家嗎?隻要你能下決心改掉不良的編程習慣,第二次考試就能及格了。
2)如果你考及格了,表明你的技術基礎不錯,希望你能虛心學習、不斷進步。如果你還沒有找到合適的工作單位,不妨到上海貝爾試一試。
3)如果你考出85分以上的好成績,你有義務和資格為你所在的團隊作“C++/C編程”培訓。希望你能和我們多多交流、相互促進。半年前我曾經發現一顆好苗子,就把他挖到我們小組來。
4)如果你在沒有任何提示的情況下考了滿分,希望你能收我做你的徒弟。
 
    編程考試結束後,請閱讀本書的正文。
    本書第一章至第六章主要論述C++/C編程風格。難度不高,但是細節比較多。別小看了,提高質量就是要從這些點點滴滴做起。世上不存在最好的編程風格,一切因需求而定。團隊開發講究風格一致,如果製定了大家認可的編程風格,那麼所有組員都要遵守。如果讀者覺得本書的編程風格比較合你的工作,那麼就采用它,不要隻看不做。人在小時候說話發音不準,寫字潦草,如果不改正,總有後悔的時候。編程也是同樣道理。
    第七章至第十一章是專題論述,技術難度比較高,看書時要積極思考。特別是第七章“內存管理”,讀了並不表示懂了,懂了並不表示就能正確使用。有一位同事看了第七章後覺得“野指針”寫得不錯,與我切磋了一把。可是過了兩周,他告訴我,他忙了兩天追查出一個Bug,想不到又是“野指針”出問題,隻好重讀第七章。
光看本書對提高編程質量是有限的,建議大家閱讀本書的參考文獻,那些都是經典名著。
 
       如果你的編程質量已經過關了,不要就此滿足。如果你想成為優秀的軟件開發人員,建議你閱讀並按照CMMI規範做事,讓自己的綜合水平上升一個台階。上海貝爾的員工可以向網絡應用事業部軟件工程研究小組索取CMMI有關資料,最好能參加培訓。
 
三、版權聲明
 
       本書的大部分內容取材於作者一年前的書籍手稿(尚未出版),現整理匯編成為上海貝爾網絡應用事業部的一個規範化文件,同時作為培訓教材。
       由於C++/C編程是眾所周知的技術,沒有秘密可言。編程的好經驗應該大家共享,我們自己也是這麼學來的。作者願意公開本書的電子文檔。
       版權聲明如下:
(1)讀者可以任意拷貝、修改本書的內容,但不可以篡改作者及所屬單位。
(2)未經作者許可,不得出版或大量印發本書。
(3)如果競爭對手公司的員工得到本書,請勿公開使用,以免發生糾紛。
       預計到2002年7月,我們將建立切合中國國情的CMMI 3級解決方案。屆時,包括本書在內的約1000頁規範將嚴格受控。
 
       歡迎讀者對本書提出批評建議
 
 
林銳,2001年7月
 
 
第1文件結構
每個C++/C程序通常分為兩個文件。一個文件用於保存程序的聲明(declaration),稱為頭文件。另一個文件用於保存程序的實現(implementation),稱為定義(definition)文件。
C++/C程序的頭文件以“.h”為後綴,C程序的定義文件以“.c”為後綴,C++程序的定義文件通常以“.cpp”為後綴(也有一些係統以“.cc”或“.cxx”為後綴)。
1.1 版權和版本的聲明
版權和版本的聲明位於頭文件和定義文件的開頭(參見示例1-1),主要內容有:
(1)版權信息。
(2)文件名稱,標識符,摘要。
(3)當前版本號,作者/修改者,完成日期。
(4)版本曆史信息。
 
/*
* Copyright (c) 2001,上海貝爾有限公司網絡應用事業部
* All rights reserved.
*
* 文件名稱:filename.h
* 文件標識:見配置管理計劃書
* 摘    要:簡要描述本文件的內容
*
* 當前版本:1.1
* 作    者:輸入作者(或修改者)名字
* 完成日期:2001年7月20日
*
* 取代版本:1.0
* 原作者 :輸入原作者(或修改者)名字
* 完成日期:2001年5月10日
*/
 
示例1-1 版權和版本的聲明
1.2 頭文件的結構
頭文件由三部分內容組成:
(1)頭文件開頭處的版權和版本聲明(參見示例1-1)。
(2)預處理塊。
(3)函數和類結構聲明等。
假設頭文件名稱為 graphics.h,頭文件的結構參見示例1-2。
 
l         【規則1-2-1為了防止頭文件被重複引用,應當用ifndef/define/endif結構產生預處理塊。
l         【規則1-2-2用 #include <filename.h> 格式來引用標準庫的頭文件(編譯器將從標準庫目錄開始搜索)。
l         【規則1-2-3用 #include “filename.h” 格式來引用非標準庫的頭文件(編譯器將從用戶的工作目錄開始搜索)。
²        【建議1-2-1頭文件中隻存放“聲明”而不存放“定義”
在C++ 語法中,類的成員函數可以在聲明的同時被定義,並且自動成為內聯函數。這雖然會帶來書寫上的方便,但卻造成了風格不一致,弊大於利。建議將成員函數的定義與聲明分開,不論該函數體有多麼小。
²        【建議1-2-2不提倡使用全局變量,盡量不要在頭文件中出現象extern int value 這類聲明。
 
// 版權和版本聲明見示例1-1,此處省略。
 
#ifndef   GRAPHICS_H // 防止graphics.h被重複引用
#define   GRAPHICS_H
 
#include <math.h>     // 引用標準庫的頭文件
#include “myheader.h”  // 引用非標準庫的頭文件
void Function1(…);   // 全局函數聲明
class Box             // 類結構聲明
{
};
#endif
示例1-2 C++/C頭文件的結構
 
1.3 定義文件的結構
定義文件有三部分內容:
(1)       定義文件開頭處的版權和版本聲明(參見示例1-1)。
(2)       對一些頭文件的引用。
(3)       程序的實現體(包括數據和代碼)。
假設定義文件的名稱為 graphics.cpp,定義文件的結構參見示例1-3。
 
// 版權和版本聲明見示例1-1,此處省略。
 
#include “graphics.h”     // 引用頭文件
 
// 全局函數的實現體
void Function1(…)
{
}
 
// 類成員函數的實現體
void Box::Draw(…)
{
}
示例1-3 C++/C定義文件的結構
1.4 頭文件的作用
早期的編程語言如Basic、Fortran沒有頭文件的概念,C++/C語言的初學者雖然會用使用頭文件,但常常不明其理。這裏對頭文件的作用略作解釋:
(1)通過頭文件來調用庫功能。在很多場合,源代碼不便(或不準)向用戶公布,隻要向用戶提供頭文件和二進製的庫即可。用戶隻需要按照頭文件中的接口聲明來調用庫功能,而不必關心接口怎麼實現的。編譯器會從庫中提取相應的代碼。
(2)頭文件能加強類型安全檢查。如果某個接口被實現或被使用時,其方式與頭文件中的聲明不一致,編譯器就會指出錯誤,這一簡單的規則能大大減輕程序員調試、改錯的負擔。
1.5 目錄結構
如果一個軟件的頭文件數目比較多(如超過十個),通常應將頭文件和定義文件分別保存於不同的目錄,以便於維護。
例如可將頭文件保存於include目錄,將定義文件保存於source目錄(可以是多級目錄)。
如果某些頭文件是私有的,它不會被用戶的程序直接引用,則沒有必要公開其“聲明”。為了加強信息隱藏,這些私有的頭文件可以和定義文件存放於同一個目錄。
 
第2程序的版式
       版式雖然不會影響程序的功能,但會影響可讀性。程序的版式追求清晰、美觀,是程序風格的重要構成因素。
可以把程序的版式比喻為“書法”。好的“書法”可讓人對程序一目了然,看得興致勃勃。差的程序“書法”如螃蟹爬行,讓人看得索然無味,更令維護者煩惱有加。請程序員們學習程序的“書法”,彌補大學計算機教育的漏洞,實在很有必要。
2.1 空行
空行起著分隔程序段落的作用。空行得體(不過多也不過少)將使程序的布局更加清晰。空行不會浪費內存,雖然打印含有空行的程序是會多消耗一些紙張,但是值得。所以不要舍不得用空行。
 
l         【規則2-1-1在每個類聲明之後、每個函數定義結束之後都要加空行。參見示例2-1(a)
l         【規則2-1-2在一個函數體內,邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。參見示例2-1(b )
 
// 空行
void Function1(…)
{
 …
}
// 空行
void Function2(…)
{
 …
}
// 空行
void Function3(…)
{
 …
}
 
// 空行
while (condition)
{
 statement1;
 // 空行
 if (condition)
 {
     statement2;
 }
 else
 {
     statement3;
 }
// 空行
 statement4;
示例2-1(a) 函數之間的空行                  示例2-1(b) 函數內部的空行
 
2.2 代碼行
l         【規則2-2-1一行代碼隻做一件事情,如隻定義一個變量,或隻寫一條語句。這樣的代碼容易閱讀,並且方便於寫注釋。
l         【規則2-2-2if、for、while、do等語句自占一行,執行語句不得緊跟其後。不論執行語句有多少都要加{}。這樣可以防止書寫失誤。
 
示例2-2(a)為風格良好的代碼行,示例2-2(b)為風格不良的代碼行。
 
 
int width;    // 寬度
int height;   // 高度
int depth;    // 深度
 
int width, height, depth; // 寬度高度深度
 
x = a + b;
y = c + d;
z = e + f;
X = a + b;   y = c + d; z = e + f;
 
if (width < height)
{
dosomething();
}
if (width < height) dosomething();
for (initialization; condition; update)
{
dosomething();
}
// 空行
other();
 
for (initialization; condition; update)
     dosomething();
other();
 
 
示例2-2(a) 風格良好的代碼行                示例2-2(b) 風格不良的代碼行
 
²        【建議2-2-1盡可能在定義變量的同時初始化該變量(就近原則)
如果變量的引用處和其定義處相隔比較遠,變量的初始化很容易被忘記。如果引用了未被初始化的變量,可能會導致程序錯誤。本建議可以減少隱患。例如
int width = 10;     // 定義並初紿化width
int height = 10; // 定義並初紿化height
int depth = 10;     // 定義並初紿化depth
 
2.3 代碼行內的空格
l         【規則2-3-1關鍵字之後要留空格。象const、virtual、inline、case 等關鍵字之後至少要留一個空格,否則無法辨析關鍵字。象if、for、while等關鍵字之後應留一個空格再跟左括號‘(’,以突出關鍵字。
l         【規則2-3-2函數名之後不要留空格,緊跟左括號‘(’,以與關鍵字區別。
l         【規則2-3-3‘(’向後緊跟,‘)’、‘,’、‘;’向前緊跟,緊跟處不留空格。
l         【規則2-3-4‘,’之後要留空格,如Function(x, y, z)。如果‘;’不是一行的結束符號,其後要留空格,如for (initialization; condition; update)。
l         【規則2-3-5賦值操作符、比較操作符、算術操作符、邏輯操作符、位域操作符,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前後應當加空格。
l         【規則2-3-6一元操作符如“!”、“~”、“++”、“--”、“&”(地址運算符)等前後不加空格。
l         【規則2-3-7象“[]”、“.”、“->”這類操作符前後不加空格。
²        【建議2-3-1對於表達式比較長的for語句和if語句,為了緊湊起見可以適當地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))
 
void Func1(int x, int y, int z);          // 良好的風格
void Func1 (int x,int y,int z);           // 不良的風格
if (year >= 2000)                         // 良好的風格
if(year>=2000)                            // 不良的風格
if ((a>=b) && (c<=d))                     // 良好的風格
if(a>=b&&c<=d)                            // 不良的風格
for (i=0; i<10; i++)                      // 良好的風格
for(i=0;i<10;i++)                         // 不良的風格
for (i = 0; I < 10; i ++)                 // 過多的空格
x = a < b ? a : b;                        // 良好的風格
x=a<b?a:b;                                // 不好的風格
int *x = &y;                              // 良好的風格 
int * x = & y;                            // 不良的風格 
array[5] = 0;                             // 不要寫成 array [ 5 ] = 0;
a.Function();                             // 不要寫成 a . Function();
b->Function();                            // 不要寫成 b -> Function();
 
示例2-3 代碼行內的空格
 
2.4 對齊
l         【規則2-4-1程序的分界符‘{’和‘}’應獨占一行並且位於同一列,同時與引用它們的語句左對齊。
l         【規則2-4-2{ }之內的代碼塊在‘{’右邊數格處左對齊。
 
示例2-4(a)為風格良好的對齊,示例2-4(b)為風格不良的對齊。
 
 
void Function(int x)
{
… // program code
}
 
void Function(int x){
… // program code
}
 
if (condition)
{
… // program code
}
else
{
… // program code
}
if (condition){
… // program code
}
else {
… // program code
}
for (initialization; condition; update)
{
… // program code
}
for (initialization; condition; update){
… // program code
}
While (condition)
{
… // program code
}
while (condition){
… // program code
}
如果出現嵌套的{},則使用縮進對齊,如:
     {
       …
          {
           …
          }
       …
}
 
示例2-4(a) 風格良好的對齊                      示例2-4(b) 風格不良的對齊
 
2.5 長行拆分
l         【規則2-5-1代碼行最大長度宜控製在70至80個字符以內。代碼行不要過長,否則眼睛看不過來,也不便於打印。
l         【規則2-5-2長表達式要在低優先級操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當的縮進,使排版整齊,語句可讀。
 
if ((very_longer_variable1 >= very_longer_variable12)
&& (very_longer_variable3 <= very_longer_variable14)
&& (very_longer_variable5 <= very_longer_variable16))
{
    dosomething();
}
virtual CMatrix CMultiplyMatrix (CMatrix leftMatrix,
                                 CMatrix rightMatrix);
 
for (very_longer_initialization;
     very_longer_condition;
     very_longer_update)
{
    dosomething();
}
示例2-5 長行的拆分
2.6 修飾符的位置
修飾符 * 和 & 應該靠近數據類型還是該靠近變量名,是個有爭議的活題。
若將修飾符 * 靠近數據類型,例如:int* x; 從語義上講此寫法比較直觀,即x是int 類型的指針。
上述寫法的弊端是容易引起誤解,例如:int* x, y; 此處y容易被誤解為指針變量。雖然將x和y分行定義可以避免誤解,但並不是人人都願意這樣做。
 
l         【規則2-6-1應當將修飾符 * 和 & 緊靠變量名
例如:
char *name;
    int   *x, y; // 此處y不會被誤解為指針
2.7 注釋
C語言的注釋符為“/*…*/”。C++語言中,程序塊的注釋常采用“/*…*/”,行注釋一般采用“//…”。注釋通常用於:
(1)版本、版權聲明;
(2)函數接口說明;
(3)重要的代碼行或段落提示。
雖然注釋有助於理解代碼,但注意不可過多地使用注釋。參見示例2-6。
 
l         【規則2-7-1注釋是對代碼的“提示”,而不是文檔。程序中的注釋不可喧賓奪主,注釋太多了會讓人眼花繚亂。注釋的花樣要少。
l         【規則2-7-2如果代碼本來就是清楚的,則不必加注釋。否則多此一舉,令人厭煩。例如
i++;     // i 加 1,多餘的注釋
l         【規則2-7-3邊寫代碼邊注釋,修改代碼同時修改相應的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除。
l         【規則2-7-4注釋應當準確、易懂,防止注釋有二義性。錯誤的注釋不但無益反而有害。
l         【規則2-7-5盡量避免在注釋中使用縮寫,特別是不常用縮寫。
l         【規則2-7-6注釋的位置應與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方。
l         【規則2-7-8當代碼比較長,特別是有多重嵌套時,應當在一些段落的結束處加注釋,便於閱讀。
 
 
/*
* 函數介紹:
* 輸入參數:
* 輸出參數:
* 返回值 :
*/
void Function(float x, float y, float z)
{
 …
}
 
if (…)
{
 …
 while (…)
 {
} // end of while
} // end of if
示例2-6 程序的注釋
 
2.8 類的版式
類可以將數據和函數封裝在一起,其中函數表示了類的行為(或稱服務)。類提供關鍵字public、protected和private,分別用於聲明哪些數據和函數是公有的、受保護的或者是私有的。這樣可以達到信息隱藏的目的,即讓類僅僅公開必須要讓外界知道的內容,而隱藏其它一切內容。我們不可以濫用類的封裝功能,不要把它當成火鍋,什麼東西都往裏扔。
類的版式主要有兩種方式:
(1)將private類型的數據寫在前麵,而將public類型的函數寫在後麵,如示例8-3(a)。采用這種版式的程序員主張類的設計“以數據為中心”,重點關注類的內部結構。
(2)將public類型的函數寫在前麵,而將private類型的數據寫在後麵,如示例8.3(b)采用這種版式的程序員主張類的設計“以行為為中心”,重點關注的是類應該提供什麼樣的接口(或服務)。
很多C++教課書受到Biarne Stroustrup第一本著作的影響,不知不覺地采用了“以數據為中心”的書寫方式,並不見得有多少道理。
我建議讀者采用“以行為為中心”的書寫方式,即首先考慮類應該提供什麼樣的函數。這是很多人的經驗——“這樣做不僅讓自己在設計類時思路清晰,而且方便別人閱讀。因為用戶最關心的是接口,誰願意先看到一堆私有數據成員!”
 
class A
{
 private:
int    i, j;
float x, y;
   
 public:
void Func1(void);
void Func2(void);
}
class A
{
 public:
void Func1(void);
void Func2(void);
 private:
int    i, j;
float x, y;
   
}
示例8.3(a) 以數據為中心版式                 示例8.3(b) 以行為為中心的版式
 
 
 
第3命名規則
比較著名的命名規則當推Microsoft公司的“匈牙利”法,該命名規則的主要思想是“在變量和函數名中加入前綴以增進人們對程序的理解”。例如所有的字符變量均以ch為前綴,若是指針變量則追加前綴p。如果一個變量由ppch開頭,則表明它是指向字符指針的指針。
“匈牙利”法最大的缺點是煩瑣,例如
int    i, j, k; 
float x, y, z;
倘若采用“匈牙利”命名規則,則應當寫成
int    iI, iJ, ik; // 前綴 i表示int類型
float fX, fY, fZ; // 前綴 f表示float類型
如此煩瑣的程序會讓絕大多數程序員無法忍受。
據考察,沒有一種命名規則可以讓所有的程序員讚同,程序設計教科書一般都不指定命名規則。命名規則對軟件產品而言並不是“成敗悠關”的事,我們不要化太多精力試圖發明世界上最好的命名規則,而應當製定一種令大多數項目成員滿意的命名規則,並在項目中貫徹實施。
3.1 共性規則
       本節論述的共性規則是被大多數程序員采納的,我們應當在遵循這些共性規則的前提下,再擴充特定的規則,如3.2節。
 
l         【規則3-1-1標識符應當直觀且可以拚讀,可望文知意,不必進行“解碼”。
標識符最好采用英文單詞或其組合,便於記憶和閱讀。切忌使用漢語拚音來命名。程序中的英文單詞一般不會太複雜,用詞應當準確。例如不要把CurrentValue寫成NowValue。
 
l         【規則3-1-2標識符的長度應當符合“min-length && max-information”原則。
幾十年前老ANSI C規定名字不準超過6個字符,現今的C++/C不再有此限製。一般來說,長名字能更好地表達含義

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

  上一篇:go Pro JavaScript Techniques第一章: 現代javscript編程
  下一篇:go 史玉柱的網遊