標籤:unix foo 輸出 風格 ase 整型 常量 著作權 進入
第1章 檔案結構
1.著作權和版本的聲明
2.標頭檔的結構
【規則1-2-1】為了防止標頭檔被重複引用,應當用ifndef/define/endif結構產生預先處理塊。
【規則1-2-2】用 #include <filename.h> 格式來引用標準庫的標頭檔(編譯器將從標準庫目錄開始搜尋)。
【規則1-2-3】用 #include “filename.h” 格式來引用非標準庫的標頭檔(編譯器將從使用者的工作目錄開始搜尋)。
【建議1-2-1】標頭檔中只存放“聲明”而不存放“定義”。
【建議1-2-2】不提倡使用全域變數,盡量不要在標頭檔中出現象extern int value 這類聲明。
3.定義檔案的結構
4.標頭檔的作用
5.目錄結構
第2章 程式的版式
1.空行
【規則2-1-1】在每個類聲明之後、每個函數定義結束之後都要加空行。
【規則2-1-2】在一個函數體內,邏揖上密切相關的語句之間不加空行,其它地方應加空行分隔。
2.程式碼
【規則2-2-1】一行代碼只做一件事情,如只定義一個變數,或唯寫一條語句。
【規則2-2-2】if、for、while、do等語句自佔一行,執行語句不得緊跟其後。
【建議2-2-1】儘可能在定義變數的同時初始化該變數(就近原則)。
3.程式碼內的空格
【規則2-3-1】關鍵字之後要留空格。
【規則2-3-2】函數名之後不要留空格,緊跟左括弧‘(’,以與關鍵字區別。
【規則2-3-3】‘(’向後緊跟,‘)’、‘,’、‘;’向前緊跟,緊跟處不留空格。
【規則2-3-4】‘,’之後要留空格,如Function(x, y, z)。
【規則2-3-5】賦值操作符、比較操作符、算術操作符、邏輯操作符、位網域作業符,如“=”、“+=” “>=”、“<=”、“+”、“*”、“%”、“&&”、“||”、“<<”,“^”等二元操作符的前後應當加空格。
【規則2-3-6】一元操作符如“!”、“~”、“++”、“--”、“&”(地址運算子)等前後不加空格。
【規則2-3-7】象“[]”、“.”、“->”這類操作符前後不加空格。
【建議2-3-1】對於運算式比較長的for語句和if語句,為了緊湊起見可以適當地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))。
4.對齊
【規則2-4-1】程式的分界符‘{’和‘}’應獨佔一行並且位於同一列,同時與引用它們的語句靠左對齊。
【規則2-4-2】{ }之內的代碼塊在‘{’右邊數格處靠左對齊。
5.長行拆分
【規則2-5-1】程式碼最大長度宜控制在70至80個字元以內。
【規則2-5-2】長運算式要在低優先順序操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。
6.修飾符的位置
【規則2-6-1】應當將修飾符 * 和 & 緊靠變數名。
7.注釋
【規則2-7-1】注釋是對代碼的“提示”,而不是文檔。程式中的注釋不可喧賓奪主,注釋太多了會讓人眼花繚亂。注釋的花樣要少。
【規則2-7-2】如果代碼本來就是清楚的,則不必加註釋。
【規則2-7-3】邊寫代碼邊注釋,修改代碼同時修改相應的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除。
【規則2-7-4】注釋應當準確、易懂,防止注釋有二義性。
【規則2-7-5】盡量避免在注釋中使用縮寫,特別是不常用縮寫。
【規則2-7-6】注釋的位置應與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方。
【規則2-7-8】當代碼比較長,特別是有多重嵌套時,應當在一些段落的結束處加註釋,便於閱讀。
8.類的版式
類的版式主要有兩種方式:
(1)將private類型的資料寫在前面,而將public類型的函數寫在後面。採用這種版式的程式員主張類的設計“以資料為中心”,重點關注類的內部結構。
(2)將public類型的函數寫在前面,而將private類型的資料寫在後面。採用這種版式的程式員主張類的設計“以行為為中心”,重點關注的是類應該提供什麼樣的介面(或服務)。
第3章 命名規則
1.共性規則
【規則3-1-1】標識符應當直觀且可以拼讀,可望文知意,不必進行“解碼”。
【規則3-1-2】標識符的長度應當符合“min-length && max-information”原則
【規則3-1-3】命名規則盡量與所採用的作業系統或開發工具的風格保持一致。
【規則3-1-4】程式中不要出現僅靠大小寫區分的相似的標識符。
【規則3-1-5】程式中不要出現標識符完全相同的局部變數和全域變數,儘管兩者的範圍不同而不會發生語法錯誤,但會使人誤解。
【規則3-1-6】變數的名字應當使用“名詞”或者“形容詞+名詞”。
【規則3-1-7】全域函數的名字應當使用“動詞”或者“動詞+名詞”(動賓片語)。
【規則3-1-8】用正確的反義片語命名具有互斥意義的變數或相反動作的函數等。
【建議3-1-1】盡量避免名字中出現數字編號,如Value1,Value2等,除非邏輯上的確需要編號。這是為了防止程式員偷懶,不肯為命名動腦筋而導致產生無意義的名字。
2.簡單的Windows應用程式命名規則
【規則3-2-1】類名和函數名用大寫字母開頭的單片語合而成。
【規則3-2-2】變數和參數用小寫字母開頭的單片語合而成。
【規則3-2-3】常量全用大寫的字母,用底線分割單詞。
【規則3-2-4】靜態變數加首碼s_(表示static)。
【規則3-2-5】如果不得已需要全域變數,則使全域變數加首碼g_(表示global)。
【規則3-2-6】類的資料成員加首碼m_(表示member),這樣可以避免資料成員與成員函數的參數同名。
【規則3-2-7】為了防止某一軟體庫中的一些標識符和其它軟體庫中的衝突,可以為各種標識符加上能反映軟體性質的首碼。
3.簡單的Unix應用程式命名規則
第4章 運算式和基本語句
1.運算子的優先順序
【規則4-1-1】如果程式碼中的運算子比較多,用括弧確定運算式的操作順序,避免使用預設的優先順序。
2.複合運算式
【規則4-2-1】不要編寫太複雜的複合運算式。
【規則4-2-2】不要有多用途的複合運算式。
【規則4-2-3】不要把程式中的複合運算式與“真正的數學運算式”混淆。
3.if 語句
【規則4-3-1】不可將布爾變數直接與TRUE、FALSE或者1、0進行比較。
【規則4-3-2】應當將整型變數用“==”或“!=”直接與0比較。
【規則4-3-3】不可將浮點變數用“==”或“!=”與任何數字比較。
【規則4-3-4】應當將指標變數用“==”或“!=”與NULL比較。
4.迴圈語句的效率
【建議4-4-1】在多重迴圈中,如果有可能,應當將最長的迴圈放在最內層,最短的迴圈放在最外層,以減少CPU跨切迴圈層的次數。
【建議4-4-2】如果迴圈體記憶體在邏輯判斷,並且迴圈次數很大,宜將邏輯判斷移到迴圈體的外面。
5.for 語句的迴圈控制變數
【規則4-5-1】不可在for 迴圈體內修改迴圈變數,防止for 迴圈失去控制。
【建議4-5-1】建議for語句的迴圈控制變數的取值採用“半開半閉區間”寫法。
6.switch語句
【規則4-6-1】每個case語句的結尾不要忘了加break,否則將導致多個分支重疊(除非有意使多個分支重疊)。
【規則4-6-2】不要忘記最後那個default分支。
7.goto語句
第5章 常量
1.為什麼需要常量
如果不使用常量,直接在程式中填寫數字或字串:
程式的可讀性(可理解性)變差;
在程式的很多地方輸入同樣的數字或字串,難保不發生書寫錯誤;
如果要修改數字或字串,則會在很多地方改動,既麻煩又容易出錯。
【規則5-1-1】 盡量使用含義直觀的常量來表示那些將在程式中多次出現的數字或字串。
2.const 與 #define的比較
const優點:
const常量有資料類型,而宏常量沒有資料類型;
有些整合化的調試工具可以對const常量進行調試,但是不能對宏常量進行調試。
【規則5-2-1】在C++ 程式中只使用const常量而不使用宏常量,即const常量完全取代宏常量。
3.常量定義規則
【規則5-3-1】需要對外公開的常量放在標頭檔中,不需要對外公開的常量放在定義檔案的頭部。
【規則5-3-2】如果某一常量與其它常量密切相關,應在定義中包含這種關係,而不應給出一些孤立的值。
4.類中的常量
用類中的枚舉常量來實現整個類中都恒定的常量。
第6章 函數設計
1.參數的規則
【規則6-1-1】參數的書寫要完整,不要貪圖省事唯寫參數的類型而省略參數名字。如果函數沒有參數,則用void填充。
【規則6-1-2】參數命名要恰當,順序要合理。
【規則6-1-3】如果參數是指標,且僅作輸入用,則應在類型前加const,以防止該指標在函數體內被意外修改。
【規則6-1-4】如果輸入參數以值傳遞的方式傳遞對象,則宜改用“const &”方式來傳遞,這樣可以省去臨時對象的構造和析構過程,從而提高效率。
【建議6-1-1】避免函數有太多的參數,參數個數盡量控制在5個以內。
【建議6-1-2】盡量不要使用類型和數目不確定的參數。
2.傳回值的規則
【規則6-2-1】不要省略傳回值的類型。
【規則6-2-2】函數名字與傳回值類型在語義上不可衝突。
【規則6-2-3】不要將正常值和錯誤標誌混在一起返回。正常值用輸出參數獲得,而錯誤標誌用return語句返回。
【建議6-2-1】有時候函數原本不需要傳回值,但為了增加靈活性如支援鏈式表達,可以附加傳回值。
【建議6-2-2】如果函數的傳回值是一個對象,有些場合用“引用傳遞”替換“值傳遞”可以提高效率。而有些場合只能用“值傳遞”而不能用“引用傳遞”,否則會出錯。
3.函數內部實現的規則
【規則6-3-1】在函數體的“入口處”,對參數的有效性進行檢查。
【規則6-3-2】在函數體的“出口處”,對return語句的正確性和效率進行檢查。
如果函數傳回值是一個對象,要考慮return語句的效率。
return String(s1 + s2);表示“建立一個臨時對象並返回它”。編譯器直接把臨時對象建立並初始化在外部儲存單元中,省去了拷貝和析構的化費,提高了效率。
String temp(s1 + s2);return temp;表示“先建立一個局部對象temp並返回它的結果”
4.其它建議
【建議6-4-1】函數的功能要單一,不要設計多用途的函數。
【建議6-4-2】函數體的規模要小,盡量控制在50行代碼之內。
【建議6-4-3】盡量避免函數帶有“記憶”功能。
【建議6-4-4】不僅要檢查輸入參數的有效性,還要檢查通過其它途徑進入函數體內的變數的有效性,例如全域變數、檔案控制代碼等。
【建議6-4-5】用於出錯處理的傳回值一定要清楚,讓使用者不容易忽視或誤解錯誤情況。
5.使用斷言
【規則6-5-1】使用斷言捕捉不應該發生的非法情況。不要混淆非法情況與錯誤情況之間的區別,後者是必然存在的並且是一定要作出處理的。
【規則6-5-2】在函數的入口處,使用斷言檢查參數的有效性(合法性)。
【建議6-5-1】在編寫函數時,要進行反覆的考查,並且自問:“我打算做哪些假定?”一旦確定了的假定,就要使用斷言對假定進行檢查。
【建議6-5-2】一般教科書都鼓勵程式員們進行防錯設計,但要記住這種編程風格可能會隱瞞錯誤。當進行防錯設計時,如果“不可能發生”的事情的確發生了,則要使用斷言進行警示。
6.引用與指標的比較
第7章 記憶體管理
1.記憶體配置方式
從靜態儲存地區分配。
在棧上建立。
從堆上分配,亦稱動態記憶體分配。
2.常見的記憶體錯誤及其對策
【規則7-2-1】用malloc或new申請記憶體之後,應該立即檢查指標值是否為NULL。防止使用指標值為NULL的記憶體。
【規則7-2-2】不要忘記為數組和動態記憶體賦初值。防止將未被初始化的記憶體作為右值使用。
【規則7-2-3】避免數組或指標的下標越界,特別要當心發生“多1”或者“少1”操作。
【規則7-2-4】動態記憶體的申請與釋放必須配對,防止記憶體流失。
【規則7-2-5】用free或delete釋放了記憶體之後,立即將指標設定為NULL,防止產生“野指標”。
3.指標與數組的對比
數組要麼在靜態儲存區被建立(如全域數組),要麼在棧上被建立。
當數組作為函數的參數進行傳遞時,該數組自動退化為同類型的指標。
4.指標參數是如何傳遞記憶體的
如果函數的參數是一個指標,不要指望用該指標去申請動態記憶體。
編譯器總是要為函數的每個參數製作臨時副本,指標參數p的副本是 _p,編譯器使 _p = p。
5.free和delete把指標怎麼啦
6.動態記憶體會被自動釋放嗎
指標消亡了,並不表示它所指的記憶體會被自動釋放。
記憶體被釋放了,並不表示指標會消亡或者成了NULL指標。
7.杜絕“野指標”
原因:
指標變數沒有被初始化。
指標p被free或者delete之後,沒有置為NULL。
指標操作超越了變數的作用範圍。
8.有了malloc/free為什麼還要new/delete
new/delete不是庫函數,是操作符。
運算子new:能完成動態記憶體分配和初始化工作。
運算子delete:能完成清理與釋放記憶體工作。
9.記憶體耗盡怎麼辦?
(1)判斷指標是否為NULL,如果是則馬上用return語句終止本函數。
(2)判斷指標是否為NULL,如果是則馬上用exit(1)終止整個程式的運行。
(3)為new和malloc設定異常處理函數
10.malloc/free 的使用要點
11.new/delete 的使用要點
12.一些心得體會
越是怕指標,就越要使用指標。不會正確使用指標,肯定算不上是合格的程式員。
必須養成“使用調試器逐步跟蹤程式”的習慣,只有這樣才能發現問題的本質。
第8章 C++函數的進階特性
1.函數重載的概念
只能靠參數而不能靠傳回值類型的不同來區分重載函數。
如果C++程式要調用已經被編譯後的C函數,該怎麼辦?
假設某個C函數的聲明如下:
void foo(int x, int y);
該函數被C編譯器編譯後在庫中的名字為_foo,而C++編譯器則會產生像_foo_int_int之類的名字用來支援函數重載和型別安全串連。由於編譯後的名字不同,C++程式不能直接調用C函數。C++提供了一個C串連交換指定符號extern“C”來解決這個問題。
當心隱式類型轉換導致重載函數產生二義性
2.成員函數的重載、覆蓋與隱藏
3.參數的預設值
【規則8-3-1】參數預設值只能出現在函數的聲明中,而不能出現在定義體中。
【規則8-3-2】如果函數有多個參數,參數只能從後向前挨個兒預設,否則將導致函數調用語句怪模怪樣。
4.運算子多載
運算子規則
所有的一元運算子建議重載為成員函數
= () [] ->只能重載為成員函數
+= -= /= *= &= |= ~= %= >>= <<=建議重載為成員函數
所有其它運算子建議重載為全域函數
5.函數內聯
慎用內聯:
如果函數體內的代碼比較長,使用內聯將導致記憶體消耗代價較高。
如果函數體內出現迴圈,那麼執行函數體內代碼的時間要比函數調用的開銷大。
6.一些心得體會
第9章 類的建構函式、解構函式與賦值函數
1.建構函式與解構函式的起源
2.建構函式的初始化表
建構函式初始化表的使用規則:
如果類存在繼承關係,衍生類別必須在其初始化表裡調用基類的建構函式。
類的const常量只能在初始化表裡被初始化。
非內部資料類型的成員對象應當採用初始化表方式初始化,以擷取更高的效率。
3.構造和析構的次序
成員對象初始化的次序完全不受它們在初始化表中次序的影響,只由成員對象在類中聲明的次序決定。
4.樣本:類String的建構函式與解構函式
5.不要輕視拷貝建構函式與賦值函數
如果不主動編寫拷貝建構函式和賦值函數,編譯器將以“位拷貝”的方式自動產生預設的函數。倘若類中含有指標變數,那麼這兩個預設的函數就隱含了錯誤。
拷貝建構函式是在對象被建立時調用的,而賦值函數只能被已經存在了的對象調用。
6.樣本:類String的拷貝建構函式與賦值函數
7.偷懶的辦法處理拷貝建構函式與賦值函數
只需將拷貝建構函式和賦值函式宣告為私人函數,不用編寫代碼。
8.如何在衍生類別中實作類別的基本函數
在編寫衍生類別的賦值函數時,注意不要忘記對基類的資料成員重新賦值。
9.一些心得體會
第10章 類的繼承與組合
1.繼承
【規則10-1-1】如果類A和類B毫不相關,不可以為了使B的功能更多些而讓B繼承A的功能和屬性。
【規則10-1-2】若在邏輯上B是A的“一種”(a kind of ),則允許B繼承A的功能和屬性。
2.組合
【規則10-2-1】若在邏輯上A是B的“一部分”(a part of),則不允許B從A派生,而是要用A和其它東西組合出B。
第11章 其它編程經驗
1.使用const提高函數的健壯性
const可以修飾函數的參數、傳回值,甚至函數的定義體。被const修飾的東西都受到強制保護,可以預防意外的變動,能提高程式的健壯性。
對於非內部資料類型的輸入參數,應該將“值傳遞”的方式改為“const引用傳遞”,目的是提高效率。例如將void Func(A a) 改為void Func(const A &a)。
對於內部資料類型的輸入參數,不要將“值傳遞”的方式改為“const引用傳遞”。否則既達不到提高效率的目的,又降低了函數的可理解性。例如void Func(int x) 不應該改為void Func(const int &x)。
任何不會修改資料成員的函數都應該聲明為const類型。
2.提高程式的效率
【規則11-2-1】不要一味地追求程式的效率,應當在滿足正確性、可靠性、健壯性、可讀性等品質因素的前提下,設法提高程式的效率。
【規則11-2-2】以提高程式的全域效率為主,提高局部效率為輔。
【規則11-2-3】在最佳化程式的效率時,應當先找出限制效率的“瓶頸”,不要在無關緊要之處最佳化。
【規則11-2-4】先最佳化資料結構和演算法,再最佳化執行代碼。
【規則11-2-5】有時候時間效率和空間效率可能對立,此時應當分析哪個更重要,作出適當的折衷。
【規則11-2-6】不要追求緊湊的代碼,因為緊湊的代碼並不能產生高效的機器碼。
3.一些有益的建議
【建議11-3-1】當心那些視覺上不易分辨的操作符發生書寫錯誤。
【建議11-3-2】變數(指標、數組)被建立之後應當及時把它們初始化,以防止把未被初始化的變數當成右值使用。
【建議11-3-3】當心變數的初值、預設值錯誤,或者精度不夠。
【建議11-3-4】當心資料類型轉換髮生錯誤。盡量使用顯式的資料類型轉換(讓人們知道發生了什麼事),避免讓編譯器輕悄悄地進行隱式的資料類型轉換。
【建議11-3-5】當心變數發生上溢或下溢,數組的下標越界。
【建議11-3-6】當心忘記編寫錯誤處理程式,當心錯誤處理程式本身有誤。
【建議11-3-7】當心檔案I/O有錯誤。
【建議11-3-8】避免編寫技巧性很高代碼。
【建議11-3-9】不要設計面面俱到、非常靈活的資料結構。
【建議11-3-10】如果原有的代碼品質比較好,盡量複用它。但是不要修補很差勁的代碼,應當重新編寫。
【建議11-3-11】盡量使用標準庫函數,不要“發明”已經存在的庫函數。
【建議11-3-12】盡量不要使用與具體硬體或軟體環境關係密切的變數。
【建議11-3-13】把編譯器的選擇項設定為最嚴格狀態。
【建議11-3-14】如果可能的話,使用PC-Lint、LogiScope等工具進行代碼審查。
<高品質C++編程指南>