中文字幕理论片,69视频免费在线观看,亚洲成人app,国产1级毛片,刘涛最大尺度戏视频,欧美亚洲美女视频,2021韩国美女仙女屋vip视频

打開APP
userphoto
未登錄

開通VIP,暢享免費電子書等14項超值服

開通VIP
C++編碼規(guī)范與指導(dǎo)
版本:1.4
作者:白楊
推薦瀏覽設(shè)置:
屏幕分辨率:≥ 1024x768
字體:中(Ctrl+鼠標滾輪設(shè)置)
最大化本窗口
文檔控制
版本號 修改時間 修改內(nèi)容 修改人 審稿人
1.0 2004-07-22 創(chuàng)建 白楊 田振軍
1.1 2004-08-05 根據(jù)審稿意建修改 白楊 田振軍、馬浩軍、葉曉峰
1.2 2004-08-09 根據(jù)審稿意建修改;新增RTTI、虛函數(shù)和虛基類的開銷分析及使用指導(dǎo) 白楊 田振軍、馬浩軍、葉曉峰
1.3 2004-08-10 重寫目錄;一些小改動 白楊
1.4 2004-08-10 新增C++成長篇 :-) 白楊
版權(quán)聲明
概述
語法高亮與字體字體
語法高亮
文件結(jié)構(gòu)文件頭注釋
頭文件
內(nèi)聯(lián)函數(shù)定義文件
實現(xiàn)文件
文件的組織結(jié)構(gòu)
命名規(guī)則類/結(jié)構(gòu)
函數(shù)
變量
常量
枚舉、聯(lián)合、typedef
宏、枚舉值
名空間
代碼風(fēng)格與版式類/結(jié)構(gòu)
函數(shù)
變量、常量
枚舉、聯(lián)合、typedef
名空間
異常
版本控制
自動工具與文檔生成
英文版
關(guān)于本規(guī)范的貫徹實施
術(shù)語表
參考文獻
C++成長篇
與我聯(lián)系
附件
常用注釋一覽
常用英文注釋一覽
文件頭例子
頭文件例子
實現(xiàn)文件例
內(nèi)聯(lián)函數(shù)定義文件例子
類/結(jié)構(gòu)的風(fēng)格與版式例子
函數(shù)的風(fēng)格與版式例子
RTTI、虛函數(shù)和虛基類的開銷分析和使用指導(dǎo)
本文檔版權(quán)歸作者所有。您可以以任意形式免費使用本文檔的任意部分,并且無需通知作者。作者對使用本文檔所造成的任何直接或者間接的損失不負任何責(zé)任。
對于任何工程項目來說,統(tǒng)一的施工標準都是保證工程質(zhì)量的重要因素??胺Q當(dāng)今人類最抽象、最復(fù)雜的工程——軟件工程,自然更加不能例外。
高品質(zhì)、易維護的軟件開發(fā)離不開清晰嚴格的編碼規(guī)范。本文檔詳細描述C++軟件開發(fā)過程中的編碼規(guī)范。本規(guī)范也適用于所有在文檔中出現(xiàn)的源碼。
除了“語法高亮”部分,本文檔中的編碼規(guī)范都以:
規(guī)則(或建議) 解釋
的格式給出,其中強制性規(guī)則使用黑色,建議性規(guī)則使用灰色。
文字是信息的載體;文字使我們能夠把個人的經(jīng)驗和思想長久的保存下來;文字使我們得以站在前人的肩膀上向前發(fā)展;文字的誕生標志著人類文明的開始……
扯的太離譜了?好吧,至少你應(yīng)該承認:
沒有文字就不可能出現(xiàn)計算機(先不管他是哪國字
) 沒有文字大家就不可能(也沒必要)學(xué)會如何寫程序 在過去、現(xiàn)在和可見的將來,使用文字符號都是編寫計算機軟件的主要方式方法
既然文字如此重要,它的長相自然會受到廣泛的關(guān)注。如今這個連MM都可以“千面”的年頭,字體的種類當(dāng)然是數(shù)不勝數(shù)。
然而,前輩先賢們曾經(jīng)用篆體教導(dǎo)偶們:
。想讓大家讀到縮進、對其正確一致,而且不出現(xiàn)亂碼的源文件,我們就要使用相互兼容的字體。
字體規(guī)范如下:
使用等寬字體 由于非等寬字體在對其等方面問題多多,任何情況下,源碼都必須使用等寬字體編輯和顯示。
每個制表符(TAB)的寬度為4個半角字符 不一致的縮進寬度會導(dǎo)致行與行之間的參差不齊,進而嚴重影響代碼的可讀性。
優(yōu)先使用Fixedsys 在Windows平臺中,應(yīng)該優(yōu)先使用字體:Fixedsys,這也是操作系統(tǒng)UI(所有的菜單、按鈕、標題欄、對話框等等)默認使用的字體。該字體的好處很多: 兼容性好:所有Windows平臺都支持該字體
顯示清晰:該字體為點陣字體,相對于矢量字體來說在顯示器中呈現(xiàn)的影像更為清晰。矢量字體雖然可以自由縮放,但這個功能對于純文本格式的程序源碼來說沒有任何實際作用。
而且當(dāng)顯示字號較?。?2pt以下)時,矢量字體還有一些明顯的缺陷:
文字的邊緣會有嚴重的凹凸感。 一些筆畫的比例也會失調(diào)。 開啟了柔化字體邊緣后,還會使文字顯得模糊不清。
說句題外話,這也是Gnome和KDE等其它GUI環(huán)境不如Windows的一個重要方面。
支持多語言:Fixedsys是UNICODE字體,支持世界上幾乎所有的文字符號。這對編寫中文注釋是很方便的。
幾乎所有的現(xiàn)代源碼編輯器均不同在程度上支持語法高亮顯示的功能。繽紛的色彩不但可以吸引MM們的目光,還可以在很大程度上幫助我們閱讀那些奧澀如咒語般的源代碼。
統(tǒng)一的語法高亮規(guī)則不僅能讓我們望色生意,還可以幫助我們閱讀沒有 編碼規(guī)范,或者規(guī)范執(zhí)行很爛的源碼。
所有在文檔中出現(xiàn)的代碼段均必須嚴格符合下表定義的語法高亮規(guī)范。在編輯源碼時,應(yīng)該根據(jù)編輯器支持的自定義選項最大限度地滿足下表定義的高亮規(guī)范。
類型 顏色 舉例
注釋
R0;G128;B0(深綠) // 注釋例子
關(guān)鍵字
R0;G0;B255(藍) typedef, int, dynamic_cast class ...
類、結(jié)構(gòu)、聯(lián)合、枚舉等其它自定義類型
R0;G0;B255(藍) class CMyClass, enum ERRTYPE, typedef int CODE ...
名空間
R0;G0;B255(藍) namespace BaiY
數(shù)字
R255;G0;B0(紅) 012 119u 0xff ...
字符、字符串
R0;G128;B128(深藍綠) "string", 'c ...
宏定義、枚舉值
R255;G128;B0(橙黃) #define UNICODE, enum { RED, GREEN, BLUE };
操作符
R136;G0;B0(棕色) < > , = + - * / ; { } ( ) [ ] ...
方法/函數(shù)
R136;G0;B0(棕色) MyFunc()
變量
R128;G128;B128(中灰色) int nMyVar;
背景
R255;G255;B255(白色)
其它
R0;G0;B0(黑色) other things(通常是一個錯誤)
所有C++的源文件均必須包含一個規(guī)范的文件頭,文件頭包含了該文件的名稱、功能概述、作者、版權(quán)和版本歷史信息等內(nèi)容。標準文件頭的格式為:   /*! @file
********************************************************************************
<PRE>
模塊名       : <文件所屬的模塊名稱>
文件名       : <文件名>
相關(guān)文件     : <與此文件相關(guān)的其它文件>
文件實現(xiàn)功能 : <描述該文件實現(xiàn)的主要功能>
作者         : <作者部門和姓名>
版本         : <當(dāng)前版本號>
--------------------------------------------------------------------------------
備注         : <其它說明>
--------------------------------------------------------------------------------
修改記錄 :
日 期        版本     修改人              修改內(nèi)容
YYYY/MM/DD   X.Y      <作者或修改者名>    <修改內(nèi)容>
</PRE>
*******************************************************************************/
如果該文件有其它需要說明的地方,還可以專門為此擴展一節(jié):
/*! @file
********************************************************************************
<PRE>
模塊名       : <文件所屬的模塊名稱>
文件名       : <文件名>
相關(guān)文件     : <與此文件相關(guān)的其它文件>
文件實現(xiàn)功能 : <描述該文件實現(xiàn)的主要功能>
作者         : <作者部門和姓名>
版本         : <當(dāng)前版本號>
--------------------------------------------------------------------------------
備注         : <其它說明>
--------------------------------------------------------------------------------
修改記錄 :
日 期        版本     修改人              修改內(nèi)容
YYYY/MM/DD   X.Y      <作者或修改者名>    <修改內(nèi)容>
</PRE>
********************************************************************************
* 項目1
- 項目1.1
- 項目1.2
================================================================================
* 項目2
- 項目2.1
- 項目2.2
....
*******************************************************************************/
每行注釋的長度都不應(yīng)該超過80個半角字符。還要注意縮進和對其,以利閱讀。
關(guān)于文件頭的完整例子,請參見:文件頭例子 關(guān)于文件頭的模板,請參見:文件頭注釋模板
頭文件通常由以下幾部分組成:   文件頭注釋 每個頭文件,無論是內(nèi)部的還是外部的,都應(yīng)該由一個規(guī)范的文件頭注釋作為開始。
預(yù)處理塊 為了防止頭文件被重復(fù)引用,應(yīng)當(dāng)用ifndef/define/endif結(jié)構(gòu)產(chǎn)生預(yù)處理塊。
函數(shù)和類/結(jié)構(gòu)的聲明等 聲明模塊的接口
需要包含的內(nèi)聯(lián)函數(shù)定義文件(如果有的話) 如果類中的內(nèi)聯(lián)函數(shù)較多,或者一個頭文件中包含多個類的定義(不推薦),可以將所有內(nèi)聯(lián)函數(shù)定義放入一個單獨的內(nèi)聯(lián)函數(shù)定義文件中,并在類聲明之后用“#include”指令把它包含進來。
頭文件的編碼規(guī)則:
引用文件的格式 用 #include <filename.h> 格式來引用標準庫和系統(tǒng)庫的頭文件(編譯器將從標準庫目錄開始搜索)。
用 #include "filename.h" 格式來引用當(dāng)前工程中的頭文件(編譯器將從該文件所在目錄開始搜索)。
分割多組接口(如果有的話) 如果在一個頭件中定義了多個類或者多組接口(不推薦),為了便于瀏覽,應(yīng)該在每個類/每組接口間使用分割帶把它們相互分開。
關(guān)于頭文件的完整例子,請參見:頭文件例子
如上所述,在內(nèi)聯(lián)函數(shù)較多的情況下,為了避免頭文件過長、版面混亂,可以將所有的內(nèi)聯(lián)函數(shù)定義移到一個單獨的文件中去,然后再用#include指令將它包含到類聲明的后面。這樣的文件稱為一個內(nèi)聯(lián)函數(shù)定義文件。
按照慣例,應(yīng)該將這個文件命名為“filename.inl”,其中“filename”與相應(yīng)的頭文件和實現(xiàn)文件相同。
內(nèi)聯(lián)函數(shù)定義文件由以下幾部分組成:
文件頭注釋 每內(nèi)聯(lián)函數(shù)定義文件都應(yīng)該由一個規(guī)范的文件頭注釋作為開始
內(nèi)聯(lián)函數(shù)定義 內(nèi)聯(lián)函數(shù)的實現(xiàn)體
內(nèi)聯(lián)函數(shù)定義文件的編碼規(guī)則:
分割多組接口(如果有的話) 如果在一個內(nèi)聯(lián)函數(shù)定義文件中定義了多個類或者多組接口的內(nèi)聯(lián)函數(shù)(不推薦),必須在每個類/每組接口間使用分割帶把它們相互分開。
文件組成中為什么沒有預(yù)處理塊? 與頭文件不同,內(nèi)聯(lián)函數(shù)定義文件通常不需要定義預(yù)處理塊,這是因為它通常被包含在與其相應(yīng)的頭文件預(yù)處理塊內(nèi)。
關(guān)于內(nèi)聯(lián)函數(shù)定義文件的完整例子,請參見:內(nèi)聯(lián)函數(shù)定義文件例子
實現(xiàn)文件包含所有數(shù)據(jù)和代碼的實現(xiàn)體。實現(xiàn)文件的格式為:   文件頭注釋 每個實現(xiàn)文件都應(yīng)該由一個規(guī)范的文件頭注釋作為開始
對配套頭文件的引用 引用聲明了此文件實現(xiàn)的類、函數(shù)及數(shù)據(jù)的頭文件
對一些僅用于實現(xiàn)的頭文件的引用(如果有的話) 將僅與實現(xiàn)相關(guān)的接口包含在實現(xiàn)文件里(而不是頭文件中)是一個非常好的編程習(xí)慣。這樣可以有效地屏蔽不應(yīng)該暴露的實現(xiàn)細節(jié),將實現(xiàn)改變對其它模塊的影響降低到最少 。
程序的實現(xiàn)體 數(shù)據(jù)和函數(shù)的定義
實現(xiàn)文件的編碼規(guī)則:
分割每個部分 在本地(靜態(tài))定義和外部定義間,以及不同接口或不同類的實現(xiàn)之間,應(yīng)使用分割帶相互分開。
關(guān)于實現(xiàn)文件的完整例子,請參見:實現(xiàn)文件例子
由于項目性質(zhì)、規(guī)模上存在著差異,不同項目間的文件組織形式差別很大。但文件、目錄組織的基本原則應(yīng)當(dāng)是一致的:使外部接口與內(nèi)部實現(xiàn)盡量分離;盡可能清晰地表達軟件的層次結(jié)構(gòu)……
為此提供兩組典型項目的文件組織結(jié)構(gòu)范例作為參考:
功能模塊/庫的文件組織形式
顯而易見,編寫功能模塊和庫的主要目的是為其它模塊提供一套完成特定功能的API,這類項目的文件組織結(jié)構(gòu)通常如下圖所示:
其中:
contrib 當(dāng)前項目所依賴的所有第三方軟件,可以按類別分設(shè)子目錄。
doc 項目文檔
include 聲明外部接口的所有頭文件和內(nèi)聯(lián)定義文件。
lib 編譯好的二進制庫文件,可以按編譯器、平臺分設(shè)子目錄。
makefile 用于編譯項目的makefile文件和project文件等??梢园淳幾g器、平臺分設(shè)子目錄。
src 所有實現(xiàn)文件和聲明內(nèi)部接口的頭文件、內(nèi)聯(lián)定義文件。可按功能劃分;支持編譯器、平臺等類別分設(shè)子目錄。
test 存放測試用代碼的目錄。
應(yīng)用程序的文件組織形式
與功能模塊不同,應(yīng)用程序是一個交付給最終用于使用的、可以獨立運行并提供完整功能的軟件產(chǎn)品,它通常不提供編程接口,應(yīng)用程序的典型文件組織形式如下圖所示:
contrib 當(dāng)前項目所依賴的所有第三方軟件,可以按類別分設(shè)子目錄。
doc 項目文檔
makefile 用于編譯項目的makefile文件和project文件等??梢园淳幾g器、平臺分設(shè)子目錄。
setup 安裝程序,以及制作安裝程序所需要的項目文件和角本。
src 所有源文件??砂垂δ軇澐郑恢С志幾g器、平臺等類別分設(shè)子目錄。
test 存放測試用代碼的目錄。
如果想要有效的管理一個稍微復(fù)雜一點的體系,針對其中事物的一套統(tǒng)一、帶層次結(jié)構(gòu)、清晰明了的命名準則就是必不可少而且非常好用的工具。
活躍在生物學(xué)、化學(xué)、軍隊、監(jiān)獄、黑社會、恐怖組織等各個領(lǐng)域內(nèi)的大量有識先輩們都曾經(jīng)無數(shù)次地以實際行動證明了以上公理的正確性。除了上帝(設(shè)它可以改變世間萬物的秩序)以外,相信沒人有實力對它不屑一顧
在軟件開發(fā)這一高度抽象而且十分復(fù)雜的活動中,命名規(guī)則的重要性更顯得尤為突出。一套定義良好并且完整的、在整個項目中統(tǒng)一使用的命名規(guī)范將大大提升源代碼的可讀性和軟件的可維護性。
在引入細節(jié)之前,先說明一下命名規(guī)范的整體原則:
同一性 在編寫一個子模塊或派生類的時候,要遵循其基類或整體模塊的命名風(fēng)格,保持命名風(fēng)格在整個模塊中的同一性。
標識符組成 標識符采用英文單詞或其組合,應(yīng)當(dāng)直觀且可以拼讀,可望文知意,用詞應(yīng)當(dāng)準確。
最小化長度 && 最大化信息量原則 在保持一個標識符意思明確的同時,應(yīng)當(dāng)盡量縮短其長度。
避免過于相似 不要出現(xiàn)僅靠大小寫區(qū)分的相似的標識符,例如“i”與“I”,“function”與“Function”等等。
避免在不同級別的作用域中重名 程序中不要出現(xiàn)名字完全相同的局部變量和全局變量,盡管兩者的作用域不同而不會發(fā)生語法錯誤,但容易使人誤解。
正確命名具有互斥意義的標識符 用正確的反義詞組命名具有互斥意義的標識符,如:"nMinValue" 和 "nMaxValue","GetName()" 和 "SetName()" ....
避免名字中出現(xiàn)數(shù)字編號 盡量避免名字中出現(xiàn)數(shù)字編號,如Value1,Value2等,除非邏輯上的確需要編號。這是為了防止程序員偷懶,不肯為命名動腦筋而導(dǎo)致產(chǎn)生無意義的名字(因為用數(shù)字編號最省事)。
除了異常類等個別情況(不希望用戶把該類看作一個普通的、正常的類之情況)外,C++類/結(jié)構(gòu)的命名應(yīng)該遵循以下準則:   C++類/結(jié)構(gòu)的命名 類的名稱都要以大寫字母“C”開頭,后跟一個或多個單詞。為便于界定,每個單詞的首字母要大寫。
推薦的組成形式 類的命名推薦用"名詞"或"形容詞+名詞"的形式,例如:"CAnalyzer", "CFastVector" ....
不同于C++類的概念,傳統(tǒng)的C結(jié)構(gòu)體只是一種將一組數(shù)據(jù)捆綁在一起的方式。傳統(tǒng)C結(jié)構(gòu)體的命名規(guī)則為:
傳統(tǒng)C結(jié)構(gòu)體的命名 傳統(tǒng)C結(jié)構(gòu)體的名稱全部由大寫字母組成,單詞間使用下劃線界定,例如:"SERVICE_STATUS", "DRIVER_INFO" ....
函數(shù)的命名 函數(shù)的名稱由一個或多個單詞組成。為便于界定,每個單詞的首字母要大寫。
推薦的組成形式 函數(shù)名應(yīng)當(dāng)使用"動詞"或者"動詞+名詞"(動賓詞組)的形式。例如:"GetName()", "SetValue()", "Erase()", "Reserve()" ....
保護成員函數(shù) 保護成員函數(shù)的開頭應(yīng)當(dāng)加上一個下劃線“_”以示區(qū)別,例如:"_SetState()" ....
私有成員函數(shù) 類似地,私有成員函數(shù)的開頭應(yīng)當(dāng)加上兩個下劃線“__”,例如:"__DestroyImp()" ....
虛函數(shù) 虛函數(shù)習(xí)慣以“Do”開頭,如:"DoRefresh()", "_DoEncryption()" ....
回調(diào)和事件處理函數(shù) 回調(diào)和事件處理函數(shù)習(xí)慣以單詞“On”開頭。例如:"_OnTimer()", "OnExit()" ....
變量應(yīng)該是程序中使用最多的標識符了,變量的命名規(guī)范可能是一套C++命名準則中最重要的部分:
變量的命名 變量名由作用域前綴+類型前綴+一個或多個單詞組成。為便于界定,每個單詞的首字母要大寫。
對于某些用途簡單明了的局部變量,也可以使用簡化的方式,如:i, j, k, x, y, z ....
作用域前綴 作用域前綴標明一個變量的可見范圍。作用域可以有如下幾種: 前綴 說明
無 局部變量
m_ 類的成員變量(member)
sm_ 類的靜態(tài)成員變量(static member)
s_ 靜態(tài)變量(static)
g_ 外部全局變量(global)
sg_ 靜態(tài)全局變量(static global)
gg_ 進程間共享的共享數(shù)據(jù)段全局變量(global global)
除非不得已,否則應(yīng)該盡可能少使用全局變量。
類型前綴 類型前綴標明一個變量的類型,可以有如下幾種: 前綴 說明
n 整型和位域變量(number)
e 枚舉型變量(enumeration)
c 字符型變量(char)
b 布爾型變量(bool)
f 浮點型變量(float)
p 指針型變量和迭代子(pointer)
pfn 特別針對指向函數(shù)的指針變量和函數(shù)對象指針(pointer of function)
g 數(shù)組(grid)
i 類的實例(instance)
對于經(jīng)常用到的類,也可以定義一些專門的前綴,如:std::string和std::wstring類的前綴可以定義為"st",std::vector類的前綴可以定義為"v"等等。
類型前綴可以組合使用,例如"gc"表示字符數(shù)組,"ppn"表示指向整型的指針的指針等等。
推薦的組成形式 變量的名字應(yīng)當(dāng)使用"名詞"或者"形容詞+名詞"。例如:"nCode", "m_nState","nMaxWidth" ....
C++中引入了對常量的支持,常量的命名規(guī)則如下:
常量的命名 常量名由類型前綴+全大寫字母組成,單詞間通過下劃線來界定,如:cDELIMITER, nMAX_BUFFER ....
類型前綴的定義與變量命名規(guī)則中的相同。
枚舉、聯(lián)合及typedef語句都是定義新類型的簡單手段,它們的命名規(guī)則為:
枚舉、聯(lián)合、typedef的命名 枚舉、聯(lián)合、typedef語句生成的類型名由全大寫字母組成,單詞間通過下劃線來界定,如:FAR_PROC, ERROR_TYPE ....
宏、枚舉值的命名 宏和枚舉值由全大寫字母組成,單詞間通過下劃線來界定,如:ERROR_UNKNOWN, OP_STOP ....
C++名空間是“類”概念的一種退化(相當(dāng)于只包含靜態(tài)成員且不能實例化的類)。它的引入為標識符名稱提供了更好的層次結(jié)構(gòu),使標識符看起來更加直觀簡捷,同時大大降低了名字沖突的可能性。
名空間的命名規(guī)則包括:
名空間的命名 名空間的名稱不應(yīng)該過長,通常都使用縮寫的形式來命名。
例如,一個圖形庫可以將其所有外部接口存放在名空間"GLIB"中,但是將其換成"GRAPHIC_LIBRARY"就不大合適。
如果碰到較長的名空間,為了簡化程序書寫,可以使用:
namespace new_name = old_long_name;
語句為其定義一個較短的別名。
代碼風(fēng)格的重要性怎么強調(diào)都不過分。一段稍長一點的無格式代碼基本上是不可讀的。
先來看一下這方面的整體原則:
空行的使用 空行起著分隔程序段落的作用??招械皿w(不過多也不過少)將使程序的布局更加清晰。空行不會浪費內(nèi)存,雖然打印含有空行的程序是會多消耗一些紙張,但是值得。所以不要舍不得用空行。 在每個類聲明之后、每個函數(shù)定義結(jié)束之后都要加2行空行。
在一個函數(shù)體內(nèi),邏揖上密切相關(guān)的語句之間不加空行,其它地方應(yīng)加空行分隔。
語句與代碼行 一行代碼只做一件事情,如只定義一個變量,或只寫一條語句。這樣的代碼容易閱讀,并且方便于寫注釋。
"if"、"for"、"while"、"do"、"try"、"catch" 等語句自占一行,執(zhí)行語句不得緊跟其后。不論執(zhí)行語句有多少都要加 "{ }" 。這樣可以防止書寫失誤。
縮進和對齊 程序的分界符 "{" 和 "}" 應(yīng)獨占一行并且位于同一列,同時與引用它們的語句左對齊。
"{ }" 之內(nèi)的代碼塊在 "{" 右邊一個制表符(4個半空格符)處左對齊。如果出現(xiàn)嵌套的 "{ }",則使用縮進對齊。
如果一條語句會對其后的多條語句產(chǎn)生影響的話,應(yīng)該只對該語句做半縮進(2個半角空格符),以突出該語句。
例如:
void
Function(int x)
{
CSessionLock iLock(*m_psemLock);
for (初始化; 終止條件; 更新)
{
// ...
}
try
{
// ...
}
catch (const exception& err)
{
// ...
}
catch (...)
{
// ...
}
// ...
}
最大長度 代碼行最大長度宜控制在70至80個字符以內(nèi)。代碼行不要過長,否則眼睛看不過來,也不便于打印。
長行拆分 長表達式要在低優(yōu)先級操作符處拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要進行適當(dāng)?shù)目s進,使排版整齊,語句可讀。
例如:
if ((very_longer_variable1 >= very_longer_variable2)
&& (very_longer_variable3 <= very_longer_variable4)
&& (very_longer_variable5 <= very_longer_variable6))
{
dosomething();
}
空格的使用 關(guān)鍵字之后要留空格。象 "const"、"virtual"、"inline"、"case" 等關(guān)鍵字之后至少要留一個空格,否則無法辨析關(guān)鍵字。象 "if"、"for"、"while"、"catch" 等關(guān)鍵字之后應(yīng)留一個空格再跟左括號 "(",以突出關(guān)鍵字。
函數(shù)名之后不要留空格,緊跟左括號 "(" ,以與關(guān)鍵字區(qū)別。
"(" 向后緊跟。而 ")"、","、";" 向前緊跟,緊跟處不留空格。
"," 之后要留空格,如 Function(x, y, z)。如果 ";" 不是一行的結(jié)束符號,其后要留空格,如 for (initialization; condition; update)。
賦值操作符、比較操作符、算術(shù)操作符、邏輯操作符、位域操作符,如"="、"+=" ">="、"<="、"+"、"*"、"%"、"&&"、"||"、"<<", "^" 等二元操作符的前后應(yīng)當(dāng)加空格。
一元操作符如 "!"、"~"、"++"、"--"、"&"(地址運算符)等前后不加空格。
象"[]"、"."、"->"這類操作符前后不加空格。
對于表達式比較長的for、do、while、switch語句和if語句,為了緊湊起見可以適當(dāng)?shù)厝サ粢恍┛崭瘢鏵or (i=0; i<10; i++)和if ((a<=b) && (c<=d))
例如:
void Func1(int x, int y, int z);    // 良好的風(fēng)格
void Func1 (int x,int y,int z);     // 不良的風(fēng)格
// ===========================================================
if (year >= 2000)         // 良好的風(fēng)格
if(year>=2000)            // 不良的風(fēng)格
if ((a>=b) && (c<=d))     // 良好的風(fēng)格
if(a>=b&&c<=d)            // 不良的風(fēng)格
// ===========================================================
for (i=0; i<10; i++)      // 良好的風(fēng)格
for(i=0;i<10;i++)         // 不良的風(fēng)格
for (i = 0; I < 10; i ++) // 過多的空格
// ===========================================================
x = a < b ? a : b;        // 良好的風(fēng)格
x=a<b?a:b;                // 不好的風(fēng)格
// ===========================================================
int* x = &y;              // 良好的風(fēng)格
int * x = & y;            // 不良的風(fēng)格
// ===========================================================
array[5] = 0;             // 不要寫成 array [ 5 ] = 0;
a.Function();             // 不要寫成 a . Function();
b->Function();            // 不要寫成 b -> Function();
修飾符的位置 為便于理解,應(yīng)當(dāng)將修飾符 "*" 和 "&" 緊靠數(shù)據(jù)類型。
例如:
char* name;
int* x,
y;    // 為避免y被誤解為指針,這里必須分行寫。
int* Function(void* p);
參見:變量、常量的風(fēng)格與版式 -> 指針或引用類型的定義和聲明
注釋 注釋的位置應(yīng)與被描述的代碼相鄰,可以放在代碼的上方或右方,不可放在下方。 邊寫代碼邊注釋,修改代碼同時修改相應(yīng)的注釋,以保證注釋與代碼的一致性。不再有用的注釋要刪除。 注釋應(yīng)當(dāng)準確、易懂,防止注釋有二義性。錯誤的注釋不但無益反而有害。 當(dāng)代碼比較長,特別是有多重嵌套時,應(yīng)當(dāng)在一些段落的結(jié)束處加注釋,便于閱讀。
與常量的比較 在與宏、常量進行 "==", "!=", ">=", "<=" 等比較運算時,應(yīng)當(dāng)將常量寫在運算符左邊,而變量寫在運算符右邊。這樣可以避免因為偶然寫錯把比較運算變成了賦值運算的問題。
例如:
if (NULL == p)  // 如果把 "==" 錯打成 "=",編譯器就會報錯
{
// ...
}
為增強代碼的可讀性而定義的宏 以下預(yù)定義宏對程序的編譯沒有任何影響,只為了增加代碼的可讀性:   宏 說明
NOTE 需要注意的代碼
TODO 尚未實現(xiàn)的接口、類、算法等
FOR_DBG 標記為調(diào)試方便而臨時增加的代碼
OK 僅用于調(diào)試的標記
例如:
TODO class CMyClass;
TODO void Function(void);
FOR_DBG cout << "...";
類是C++中最重要也是使用頻率最高的新特性之一。類的版式好壞將極大地影響代碼品質(zhì)。   注釋頭與類聲明 與文件一樣,每個類應(yīng)當(dāng)有一個注釋頭用來說明該類的各個方面。
類聲明換行緊跟在注釋頭后面,"class" 關(guān)鍵字由行首開始書寫,后跟類名稱。界定符 "{" 和 "};" 應(yīng)獨占一行,并與 "class" 關(guān)鍵字左對其。
/*! @class
********************************************************************************
<PRE>
類名稱   : CXXX
功能     : <簡要說明該類所完成的功能>
異常類   : <屬于該類的異常類(如果有的話)>
--------------------------------------------------------------------------------
備注     : <使用該類時需要注意的問題(如果有的話)>
典型用法 : <如果該類的使用方法較復(fù)雜或特殊,給出典型的代碼例子>
--------------------------------------------------------------------------------
作者     : <xxx>
</PRE>
*******************************************************************************/
class CXXX
{
// ...
};
對于功能明顯的簡單類(接口小于10個),也可以使用簡單的單行注釋頭:
//! <簡要說明該類所完成的功能>
class CXXX
{
// ...
};
繼承 基類直接跟在類名稱之后,不換行,訪問說明符(public, private, 或protected)不可省略。如:   class CXXX : public CAAA, private CBBB
{
// ...
};
以行為為中心 沒人喜歡上來就看到一大堆私有數(shù)據(jù),大多數(shù)用戶關(guān)心的是類的接口與其提供的服務(wù),而不是其實現(xiàn)。
所以應(yīng)當(dāng)將公有的定義和成員放在類聲明的最前面,保護的放在中間,而私有的擺在最后。
訪問說明符 訪問說明符(public, private, 或protected)應(yīng)該獨占一行,并與類聲明中的‘class’關(guān)鍵字左對其。
類成員的聲明版式 對于比較復(fù)雜(成員多于20個)的類,其成員必須分類聲明。
每類成員的聲明由訪問說明符(public, private, 或protected)+ 全行注釋開始。注釋不滿全行(80個半角字符)的,由 "/" 字符補齊,最后一個 "/" 字符與注釋間要留一個半角空格符。
如果一類聲明中有很多組功能不同的成員,還應(yīng)該用
每個成員的聲明都應(yīng)該由 "class" 關(guān)鍵字開始向右縮進一個制表符(4個半角空格符),成員之間左對其。
例如:
class CXXX
{
public:
/////////////////////////////////////////////////////////////////////// 類型定義
typedef vector<string> VSTR;
public:
///////////////////////////////////////////////////////////// 構(gòu)造、析構(gòu)、初始化
CXXX();
~CXXX();
public:
/////////////////////////////////////////////////////////////////////// 公用方法
// [[ 功能組1
void Function1(void) const;
long Function2(IN int n);
// ]] 功能組1
// [[ 功能組2
void Function3(void) const;
bool Function4(OUT int& n);
// ]] 功能組2
private:
/////////////////////////////////////////////////////////////////////////// 屬性
// ...
private:
///////////////////////////////////////////////////////////////////// 禁用的方法
// 禁止復(fù)制
CXXX(IN const CXXX& rhs);
CXXX& operator=(IN const CXXX& rhs);
};
正確地使用const和mutable 把不改變對象邏輯狀態(tài)的成員都標記為const成員不僅有利于用戶對成員的理解,更可以最大化對象使用方式的靈活性及合理性(比如通過const指針或const引用的形式傳遞一個對象)。
如果某個屬性的改變并不影響該對象邏輯上的狀態(tài),而且這個屬性需要在const方法中被改變,則該屬性應(yīng)該標記為 "mutable"。
例如:
class CString
{
public:
//! 查找一個子串,find()不會改變字符串的值所以為const函數(shù)
int find(IN const CString& str) const;
// ...
private:
// 最后一次錯誤值,改動這個值不會影響對象的邏輯狀態(tài),
// 像find()這樣的const函數(shù)也可能修改這個值
mutable int m_nLastError;
// ...
};
嵌套的類聲明 在相應(yīng)的邏輯關(guān)系確實存在時,類聲明可以嵌套。嵌套類可以使用簡單的單行注釋頭:   class CXXX
{
//! 嵌套類說明
calss CYYY
{
// ...
};
};
初始化列表 應(yīng)當(dāng)盡可能通過構(gòu)造函數(shù)的初始化列表來初始化成員和基類。初始化列表至少獨占一行,并且與構(gòu)造函數(shù)的定義保持一個制表符(4個半角空格)的縮進。
例如:
CXXX::CXXXX(IN int nA, IN bool bB)
: m_nA(nA), m_bB(bB)
{
// ...
};
初始化列表的書寫順序應(yīng)當(dāng)與對象的構(gòu)造順序一致,即:先按照聲明順序?qū)懟惓跏蓟?,再按照聲明順序?qū)懗蓡T初始化。
如果一個成員 "a" 需要使用另一個成員 "b" 來初始化,則 "b" 必須在 "a" 之前聲明,否則將會產(chǎn)生運行時錯誤(有些編譯器會給出警告)。
例如:
// ...
class CXXXX : public CAA, public CBB
{
// ...
CYY m_iA;
CZZ m_iB;  // m_iA必須在m_iB之前聲明
};
CXXX::CXXXX(IN int nA, IN int nB, IN bool bC)
: CAA(nA), CBB(nB), m_iA(bC), m_iB(m_iA) // 先基類,后成員,
// 分別按照聲明順序書寫
{
// ...
};
內(nèi)聯(lián)函數(shù)的實現(xiàn)體 定義在類聲明之中的函數(shù)將自動成為內(nèi)聯(lián)函數(shù)。但為了使類的聲明更為清晰明了,應(yīng)盡量避免直接在聲明中直接定義成員函數(shù)的編程風(fēng)格。鼓勵使用 "inline" 關(guān)鍵字將內(nèi)聯(lián)函數(shù)放在類聲明的外部定義。
關(guān)于類聲明的例子,請參見:
類/結(jié)構(gòu)的風(fēng)格與版式例子
關(guān)于類聲明的模板,請參見:類聲明模板
函數(shù)是程序執(zhí)行的最小單位,任何一個有效的C/C++程序都少不了函數(shù)。   函數(shù)原型 函數(shù)原型的格式為:   [存儲類] 返回值類型
[名空間或類]::函數(shù)名(參數(shù)列表) [const說明符] [異常過濾器]
例如:
static inline void
Function1(void)
int
CSem::Function2(IN const char* pcName) const throw(Exp)
其中:
以 "[ ]" 括住的為可選項目。
除了構(gòu)造/析構(gòu)函數(shù)外,"返回值類型" 和 "參數(shù)列表" 項不可省略(可以為 "void" )。
"const說明符" 僅用于成員函數(shù)中
"存儲類", "參數(shù)列表" 和 "異常過濾器" 的說明見下文
函數(shù)聲明 函數(shù)聲明的格式為:   //! 函數(shù)功能簡單說明(可選)
函數(shù)原型;
例如:
//! 執(zhí)行某某操作
static void
Function(void);
函數(shù)聲明和其它代碼間要有空行分割。
聲明成員函數(shù)時,為了緊湊,返回值類型和函數(shù)名之間不用換行,也可以適當(dāng)減少聲明間的空行。
函數(shù)定義 函數(shù)定義使用如下格式:
/*! @function
********************************************************************************
<PRE>
函數(shù)名   : <函數(shù)名>
功能     : <函數(shù)實現(xiàn)功能>
參數(shù)     : <參數(shù)類表及說明(如果有的話),格式為:>
[IN|OUT] 參數(shù)1 : 參數(shù)說明
[IN|OUT] 參數(shù)2 : 參數(shù)說明
...
返回值   : <函數(shù)返回值的意義(如果有的話)>
拋出異常 : <可能拋出的異常及其說明(如果有的話),格式為:>
類型1 : 說明
類型2 : 說明
...
--------------------------------------------------------------------------------
備注     : <其它注意事項(如果有的話)>
典型用法 : <如果該函數(shù)的使用方法較復(fù)雜或特殊,給出典型的代碼例子>
--------------------------------------------------------------------------------
作者     : <xxx>
</PRE>
*******************************************************************************/
函數(shù)原型
{
// ...
}
對于返回值、參數(shù)意義都很明確簡單函數(shù)(代碼不超過20行),也可以使用單行函數(shù)頭:
//! 函數(shù)實現(xiàn)功能
函數(shù)原型
{
// ...
}
函數(shù)定義和其它代碼之間至少分開2行空行。
參數(shù)描述宏 以下預(yù)定義宏對程序的編譯沒有任何影響,只為了增強對參數(shù)的理解:   宏 說明
IN 輸入?yún)?shù)
OUT 輸出參數(shù)
OPTIONAL 可選參數(shù)-通常指可以為NULL的指針參數(shù),帶默認值的參數(shù)不需要這樣標明
RESERVED 這個參數(shù)當(dāng)前未被支持,留待以后擴展
OWNER 獲得參數(shù)的所有權(quán),調(diào)用者不再負責(zé)銷毀參數(shù)指定的對象
UNUSED 標明這個參數(shù)在此版本中已不再使用
CHANGED 參數(shù)類型發(fā)出變化
ADDED 新增的參數(shù)
NOTE 需要注意的參數(shù)-參數(shù)意義發(fā)生變化
其中:
除了空參數(shù) "void" 以外,每個參數(shù)左側(cè)都必須有 "IN" 和/或 "OUT" 修飾
既輸入又輸出的參數(shù)應(yīng)記為:"IN OUT",而不是 "OUT IN"
IN/OUT的左側(cè)還可以根據(jù)需要加入一個或多個上表中列出的其它宏
參數(shù)描述宏的使用思想是:只要一個宏可以用在指定參數(shù)上(即:對這個參數(shù)來說,用這個描述宏修飾它是貼切的),那么就應(yīng)當(dāng)使用它。
也就是說,應(yīng)該把能用的描述宏都用上,以期盡量具體地描述一個參數(shù)。
參數(shù)列表 參數(shù)列表的格式為:   參數(shù)描述宏1 參數(shù)類型1 參數(shù)1, 參數(shù)描述宏2 參數(shù)類型2 參數(shù)2, ...
例如:
IN const int nCode, OUT string& nName
OWNER IN CDatabase* pDB, OPTIONAL IN OUT int* pRecordCount = NULL
IN OUT string& stRuleList, RESERVED IN int nOperate = 0
...
其中:
"參數(shù)描述宏" 見上文
參數(shù)命名規(guī)范與變量的命名規(guī)范相同
存儲類 "extern", "static", "inline" 等函數(shù)存儲類說明應(yīng)該在聲明和定義中一致并且顯式地使用。不允許隱式地使用一個類型聲明,也不允許一個類型聲明僅存在于函數(shù)的聲明或定義中。
成員函數(shù)的存儲類 由于C++語言的限制,成員函數(shù)的 "static", "virtual", "explicit" 等存儲類說明不允許出現(xiàn)在函數(shù)定義中。
但是為了明確起見,這些存儲類應(yīng)以注釋的形式在定義中給出。
例如:
/*virtual*/ CThread::EXITCODE
CSrvCtl::CWrkTrd::Entry(void)
{
// ...
}
/*static*/ inline void
stringEx::regex_free(IN OUT void*& pRegEx)
{
// ...
}
特別地,為縮短聲明的長度,"inline" 關(guān)鍵字可以在成員函數(shù)聲明中省略。
默認參數(shù) 類似地,參數(shù)的默認值只能出現(xiàn)在函數(shù)聲明中,但是為了明確起見,這些默認值應(yīng)以注釋的形式在定義中給出。
例如:
bool
stringEx::regex_find(OUT VREGEXRESULT& vResult,
IN stringEx stRegEx,
IN size_t nIndex      /*= 0*/,
IN size_t nStartPos   /*= 0*/,
IN bool bNoCase       /*= false*/,
IN bool bNewLine      /*= true*/,
IN bool bExtended     /*= true*/,
IN bool bNotBOL       /*= false*/,
IN bool bNotEOL       /*= false*/,
IN bool bUsePerlStyle /*= false*/) const
{
// ...
}
異常過濾器 對于任何肯能拋出異常的函數(shù),必須在其聲明和定義中顯式地指定異常過濾器,并在過濾器中列舉該函數(shù)可能拋出的異常。
例如:
int
Function(IN const char* pcName) throw(byExp, exception);
代碼段注釋 如果函數(shù)體中的代碼較長,應(yīng)該根據(jù)功能不同將其分段。代碼段間以空行分離,并且每段代碼都以代碼段分割注釋作為開始。
例如:
void
CXXX::Function(IN void* pmodAddr)
{
if (NULL == pmodAddr)
return;
{ CSessionLock iLock(*sm_hSELock);
// =====================================================================
// = 判斷指定模塊是不是剛剛被裝入,由于在NT系列平臺中,“A”系列函數(shù)都是
// = 由“W”系列函數(shù)實現(xiàn)的。所以可能會有一次LoadLibrary產(chǎn)生多次本函數(shù)調(diào)
// = 用的情況。為了增加效率,特設(shè)此靜態(tài)變量判斷上次調(diào)用是否與本次相同。
static PVOID pLastLoadedModule = NULL;
if (pLastLoadedModule == pmodAddr)
{
return;  // 相同,忽略這次調(diào)用
}
pLastLoadedModule = pmodAddr;
// =====================================================================
// = 檢查這個模塊是否在旁路模塊表中
stringEx stModName;
if (!BaiY_IMP::GetModuleNameByAddress(pmodAddr, stModName))
{
return;
}
if (CHookProc::sm_sstByPassModTbl.find(stModName)
!= CHookProc::sm_sstByPassModTbl.end())
{
return;
}
// =====================================================================
// = 在這個模塊中HOOK所有存在于HOOK函數(shù)表中的函數(shù)
PROCTBL::iterator p;
for (p=sm_iProcTbl.begin(); p!=sm_iProcTbl.end(); ++p)
{
p->HookOneModule(pmodAddr);
}
} // SessionLock
}
明顯地,如果需要反復(fù)用到一段代碼的話,這段代碼就應(yīng)當(dāng)作為一個函數(shù)實現(xiàn)。
當(dāng)一個函數(shù)過長時(超過100行),為了便于閱讀和理解,也應(yīng)當(dāng)將其中的一些代碼段實現(xiàn)為單獨的函數(shù)。
調(diào)用系統(tǒng)API 所有系統(tǒng)API調(diào)用前都要加上全局名稱解析符 "::"。
例如:
::MessageBoxA(NULL, gcErrorMsg, "!FATAL ERROR!", MB_ICONSTOP|MB_OK);
if (0 == ::GetTempFileName(m_basedir.c_str(), byT("bai"), 0, stR.ref()))
{
// ...
}
關(guān)于函數(shù)的例子,請參見:函數(shù)的風(fēng)格與版式例子
關(guān)于函數(shù)的模板,請參見:函數(shù)模板
聲明格式 變量、常量的聲明格式如下:   [存儲類] 類型 變量名;
其中:
以 "[ ]" 括住的為可選項目。
"存儲類" 的說明見下文
定義格式 變量、常量的定義格式如下:   [存儲類] 類型 變量名 = 初始值;
其中:
以 "[ ]" 括住的為可選項目。
"存儲類" 的說明見下文
存儲類 除 "auto" 類型以外,諸如 "extern", "static", "register", "volatile" 等存儲類均不可省略,且必須在聲明和定義中一致地使用(即:不允許僅在聲明或定義中使用)。
成員變量的存儲類 由于C++語言的限制,成員變量的 "static" 等存儲類說明不允許出現(xiàn)在變量定義中。
但是為了明確起見,這些存儲類應(yīng)以注釋的形式在定義中給出。
例如:
/*static*/ int CThread::sm_nPID = 0;
指針或引用類型的定義和聲明 在聲明和定義多個指針或引用變量/常量時,每個變量至少占一行。例如:   int* pn1,
* pn2 = NULL,
* pn3;
char* pc1;
char* pc2;
char* pc3;
// 錯誤的寫法:
int* pn11, *pn12, *pn13;
常指針和指針常量 聲明/定義一個常指針(指向常量的指針)時,"const" 關(guān)鍵字一律放在類型說明的左側(cè)。
聲明/定義一個指針常量(指針本身不能改變)時, "const" 關(guān)鍵字一律放在變量左側(cè)、類型右側(cè)。
例如:
const char* pc1;       // 常指針
char* const pc2;       // 指針常量
const char* const pc3; // 常指針常量
// 錯誤的寫法:
char const* pc1;  // 與 const char* pc1 含義相同,但不允許這樣寫
全局變量、常量的注釋 全局變量、常量的注釋獨占一行,并用 "http://!" 開頭。
例如:
//! 當(dāng)前進程的ID
static int sg_nPID = 0;
//! 分割符
static const char* pcDTR = "\\/";
類型轉(zhuǎn)換 禁止使用C風(fēng)格的 "(類型)" 格式轉(zhuǎn)換,應(yīng)當(dāng)優(yōu)先使用C++的 "xxx_cast" 風(fēng)格的類型轉(zhuǎn)換。C++風(fēng)格的類型轉(zhuǎn)換可以提供豐富的含義和功能,以及更好的類型檢查機制,這對代碼的閱讀、修改、除錯和移植有很大的幫助。其中:   static_cast static_cast用于編譯器認可的,安全的靜態(tài)轉(zhuǎn)換,比如將 "char" 轉(zhuǎn)為 "int" 等等。該操作在編譯時完成
reinterpret_cast reinterpret_cast用于編譯器不認可的,不安全的靜態(tài)轉(zhuǎn)換,比如將 "int*" 轉(zhuǎn)為 "int" 等等。這種轉(zhuǎn)換有可能產(chǎn)生移植性方面的問題,該操作在編譯時完成
const_cast const_cast用于將一個常量轉(zhuǎn)化為相應(yīng)類型的變量,比如將 "const char*" 轉(zhuǎn)換成 "char*" 等等。這種轉(zhuǎn)換通常伴隨潛在的錯誤。該操作在編譯時完成
dynamic_cast dynamic_cast是C++RTTI機制的重要體現(xiàn),用于在類層次結(jié)構(gòu)中漫游。dynamic_cast可以對指針和引用進行自由度很高的向上、向下和交叉轉(zhuǎn)換。被正確使用的dynamic_cast操作將在運行時完成
此外,對于定義了 單參構(gòu)造函數(shù) 或 類型轉(zhuǎn)換操作 的類來說,應(yīng)當(dāng)優(yōu)先使用構(gòu)造函數(shù)風(fēng)格的類型轉(zhuǎn)換,如:"string("test")" 等等。
通常來說,"xxx_cast" 格式的轉(zhuǎn)換與構(gòu)造函數(shù)風(fēng)格的類型轉(zhuǎn)換之間,最大的區(qū)別在于:構(gòu)造函數(shù)風(fēng)格的轉(zhuǎn)換通常會生成新的臨時對象,可能伴隨相當(dāng)?shù)臅r間和空間開銷。
而 "xxx_cast" 格式的轉(zhuǎn)換只是告訴編譯器,將指定內(nèi)存中的數(shù)據(jù)當(dāng)作另一種類型的數(shù)據(jù)看待,這些操作一般在編譯時完成,不會對程序的運行產(chǎn)生額外開銷。當(dāng)然,"dynamic_cast" 則是一個例外。
參見:RTTI、虛函數(shù)和虛基類的開銷分析和使用指導(dǎo)
枚舉、聯(lián)合的定義格式 枚舉、聯(lián)合的定義格式為:   //! 說明(可選)
enum|union 名稱
{
內(nèi)容
};
例如:
//! 服務(wù)的狀態(tài)
enum SRVSTATE
{
SRV_INVALID  = 0,
SRV_STARTING = 1,
SRV_STARTED,
SRV_PAUSING,
SRV_PAUSED,
SRV_STOPPING,
SRV_STOPPED
};
//! 32位整數(shù)
union INT32
{
unsigned char    cByte[4];
unsigned short   nShort[2];
unsigned long    nFull;
};
typedef的定義格式 typedef 的定義格式為:   //! 說明(可選)
typedef 原類型 新類型;
例如:
//! 返回值類型
typedef int EXITCODE;
//! 字符串?dāng)?shù)組類型
typedef vector<string> VSTR;
何時使用宏 應(yīng)當(dāng)盡量減少宏的使用,在所有可能的地方都使用常量和內(nèi)聯(lián)函數(shù)來代替宏 。
邊界效應(yīng) 使用宏的時候應(yīng)當(dāng)注意邊界效應(yīng),例如,以下代碼將會得出錯誤的結(jié)果:   #define PLUS(x,y) x+y
cout << PLUS(1,1) * 2;
以上程序的執(zhí)行結(jié)果將會是 "3",而不是 "4",因為 "PLUS(1,1) * 2" 表達式將會被展開為:"1 + 1 * 2"。
在定義宏的時候,只要允許,就應(yīng)該為它的替換內(nèi)容括上 "( )" 或 "{ }"。例如:
#define PLUS(x,y) (x+y)
#define SAFEDELETE(x) {delete x; x=0}
名空間的使用 名空間可以避免名字沖突、分組不同的接口以及簡化命名規(guī)則。應(yīng)當(dāng)盡可能地將所有接口都放入適當(dāng)?shù)拿挚臻g中。
將實現(xiàn)和界面分離 提供給用戶的界面和用于實現(xiàn)的細節(jié)應(yīng)當(dāng)分別放入不同的名空間中。
例如:如果將一個軟件模塊的所有接口都放在名空間 "MODULE" 中,那么這個模塊的所有實現(xiàn)細節(jié)就可以放入名空間 "MODULE_IMP" 中。
異常使C++的錯誤處理更為結(jié)構(gòu)化;錯誤傳遞和故障恢復(fù)更為安全簡便;也使錯誤處理代碼和其它代碼間有效的分離開來。
何時使用異常 異常機制只用在發(fā)生錯誤的時候,僅在發(fā)生錯誤時才應(yīng)當(dāng)拋出異常。這樣做有助于錯誤處理和程序動作兩者間的分離,增強程序的結(jié)構(gòu)化,還保證了程序的執(zhí)行效率。
確定某一狀況是否算作錯誤有時會很困難。比如:未搜索到某個字符串、等待一個信號量超時等等狀態(tài),在某些情況下可能并不算作一個錯誤,而在另一些情況下可能就是一個致命錯誤。
有鑒于此,僅當(dāng)某狀況必為一個錯誤時(比如:分配存儲失敗、創(chuàng)建信號量失敗等),才應(yīng)該拋出一個異常。而對另外一些模棱兩可的情況,就應(yīng)當(dāng)使用返回值等其它手段報告 。
用異常代替goto等其它錯誤處理手段 曾經(jīng)被廣泛使用的傳統(tǒng)錯誤處理手段有g(shù)oto風(fēng)格和do...while風(fēng)格等,以下是一個goto風(fēng)格的例子:
//! 使用goto進行錯誤處理的例子
bool
Function(void)
{
int nCode, i;
bool r = false;
// ...
if (!Operation1(nCode))
{
goto onerr;
}
try
{
Operation2(i);
}
catch (...)
{
r = true;
goto onerr;
}
r = true;
onerr:
// ... 清理代碼
return r;
}
由上例可見,goto風(fēng)格的錯誤處理至少存在問題如下:
錯誤處理代碼和其它代碼混雜在一起,使程序不夠清晰易讀
變量必須在 "goto" 之前聲明,違反就近原則
多處跳轉(zhuǎn)的使用破壞程序的結(jié)構(gòu)化,影響程序的可讀性,使程序容易出錯
對每個會拋出異常的操作都需要用額外的try...catch塊檢測和處理
稍微復(fù)雜一點的分類錯誤處理要使用多個標號和不同的goto跳轉(zhuǎn)(如: "onOp1Err", "onOp2Err" ...)。這將使程序變得無法理解和錯誤百出。
再來看看do...while風(fēng)格的錯誤處理:
//! 使用do...while進行錯誤處理的例子
bool
Function(void)
{
int nCode, i;
bool r = false;
// ...
do
{
if (!Operation1(nCode))
{
break;
}
do
{
try
{
Operation2(i);
}
catch (...)
{
r = true;
break;
}
} while (Operation3())
r = true;
} while (false);
// ... 清理代碼
return r;
}
與goto風(fēng)格的錯誤處理相似:
錯誤處理代碼和其它代碼嚴重混雜,使程序非常難以理解
無法進行分類錯誤處理
對每個會拋出異常的操作都需要用額外的try...catch塊檢測和處理
此外,還有一種更為糟糕的錯誤處理風(fēng)格——直接在出錯的位置完成錯誤處理:
//! 直接進行錯誤處理的例子
bool
Function(void)
{
int nCode, i;
// ...
if (!Operation1(nCode))
{
// ... 清理代碼
return false;
}
try
{
Operation2(i);
}
catch (...)
{
// ... 清理代碼
return true;
}
// ...
// ... 清理代碼
return true;
}
這種錯誤處理方式所帶來的隱患可以說是無窮無盡,這里不再列舉。
與傳統(tǒng)的錯誤處理方法不同,C++的異常機制很好地解決了以上問題。使用異常做出錯處理時,可以將大部分動作都包含在一個try塊中,并以不同的catch塊捕獲和處理不同的錯誤:
//! 使用異常進行錯誤處理的例子
bool
Function(void)
{
int nCode, i;
bool r = false;
try
{
if (!Operation1(nCode))
{
throw false;
}
Operation2(i);
}
catch (bool err)
{
// ...
r = err;
}
catch (const excption& err)
{
// ... excption類錯誤處理
}
catch (...)
{
// ... 處理其它錯誤
}
// ... 清理代碼
return r;
}
以上代碼示例中,錯誤處理和動作代碼完全分離,錯誤分類清晰明了,好處不言而喻。
構(gòu)造函數(shù)中的異常 在構(gòu)造函數(shù)中拋出異常將中止對象的構(gòu)造,這將產(chǎn)生一個沒有被完整構(gòu)造的對象。
對于C++來說,這種不完整的對象將被視為并為創(chuàng)建而不被認可,也意味著其析構(gòu)函數(shù)永遠不會被調(diào)用。這個行為本身無可非議,就好像公安局不會為一個流產(chǎn)的嬰兒發(fā)戶口一樣。但是這有時也會產(chǎn)生一些問題,例如:
class CSample
{
// ...
char* m_pc;
};
CSample::CSample()
{
m_pc = new char[256];
// ...
throw -1;  // m_pc將永遠不會被釋放
}
CSample::~CSample()  // 析構(gòu)函數(shù)不會被調(diào)用
{
delete m_pc;
}
解決這個問題的方法是在拋出異常以前釋放任何已被申請的資源。一種更好的方法是使用“資源申請即初始化”的類型(如:句柄類、靈巧指針類等等)來代替一般的指針類型,如:
templete <class T>
struct CAutoPtr
{
CAutoPtr(T* p = NULL) : m_p(p) {};
~CAutoPtr() {delete m_p;}
T* operator=(T* rhs)
{
if (rhs == m_p)
return m_p;
delete m_p;
m_p = rhs;
return m_p;
}
// ...
T* m_p;
};
class CSample
{
// ...
CAutoPtr<char> m_hc;
};
CSample::CSample()
{
m_hc = new char[256];
// ...
throw -1;  // 由于m_hc已經(jīng)成功構(gòu)造,m_hc.~CAutoPtr()將會
// 被調(diào)用,所以申請的內(nèi)存將被釋放
}
注意:上述CAutoPtr類僅用于示范,對于所有權(quán)語義的通用自動指針,應(yīng)該使用C++標準庫中的 "auto_ptr" 模板類。對于帶引用計數(shù)和自定義銷毀策略的通用句柄類,可以使用白楊工具庫中的 "CHandle" 模板類。
析構(gòu)函數(shù)中的異常 析構(gòu)函數(shù)中的異常可能在2種情況下被拋出: 對象被正常析構(gòu)時 在一個異常被拋出后的退棧過程中——異常處理機制退出一個作用域,其中所有對象的析構(gòu)函數(shù)都將被調(diào)用。
由于C++不支持異常的異常,上述第二種情況將導(dǎo)致一個致命錯誤,并使程序中止執(zhí)行。例如:
class CSample
{
~CSample();
// ...
};
CSample::~CSample()
{
// ...
throw -1;  // 在 "throw false" 的過程中再次拋出異常
}
void
Function(void)
{
CSample iTest;
throw false;  // 錯誤,iTest.~CSample()中也會拋出異常
}
如果必須要在析構(gòu)函數(shù)中拋出異常,則應(yīng)該在異常拋出前用 "std::uncaught_exception()" 事先判斷當(dāng)前是否存在已被拋出但尚未捕獲的異常。例如:
class CSample
{
~CSample();
// ...
};
CSample::~CSample()
{
// ...
if (!std::uncaught_exception()) // 沒有尚未捕獲的異常
{
throw -1;  // 拋出異常
}
}
void
Function(void)
{
CSample iTest;
throw false;  // 可以,iTest.~CSample()不會拋出異常
}
異常的組織 異常類型應(yīng)該以繼承的方式組織成一個層次結(jié)構(gòu),這將使以不同粒度分類處理錯誤成為可能。
通常,某個軟件生產(chǎn)組織的所有異常都從一個公共的基類派生出來。而每個類的異常則從該類所屬模塊的公共異?;愔信缮@纾?div style="height:15px;">
異常捕獲和重新拋出 異常捕獲器的書寫順序應(yīng)當(dāng)由特殊到一般(先子類后基類),最后才是處理所有異常的捕獲器("catch(...)")。否則將使某些異常捕獲器永遠不會被執(zhí)行。
為避免捕獲到的異常被截斷,異常捕獲器中的參數(shù)類型應(yīng)當(dāng)為常引用型或指針型。
在某級異常捕獲器中無法被徹底處理的錯誤可以被重新拋出。重新拋出采用一個不帶運算對象的 "throw" 語句。重新拋出的對象就是剛剛被拋出的那個異常,而不是處理器捕獲到的(有可能被截斷的)異常。
例如:
try
{
// ...
}
// 公鑰加密錯誤
catch (const CPubKeyCipher::Exp& err)
{
if (可以恢復(fù))
{
// 恢復(fù)錯誤
}
else
{
// 完成能做到的事情
throw;  // 重新拋出
}
}
// 處理其它加密庫錯誤
catch (const CryptoExp& err)
{
// ...
}
// 處理其它本公司模塊拋出的錯誤
catch (const CompanyExp& err)
{
// ...
}
// 處理 dynamic_cast 錯誤
catch (const bad_cast& err)
{
// ...
}
// 處理其它標準庫錯誤
catch (const exception& err)
{
// ...
}
// 處理所有其它錯誤
catch (...)
{
throw;  // 重新拋出
}
異常和效率 對于幾乎所有現(xiàn)代編譯器來說,在不拋出異常的情況下,異常處理的實現(xiàn)在運行時不會有任何額外開銷,也就是說:正常情況下,異常機制比傳統(tǒng)的通過返回值判斷錯誤的開銷還來得小。
相對于函數(shù)返回和調(diào)用的開銷來講,異常拋出和捕獲的開銷通常會來得大一些。不過錯誤處理代碼通常不會頻繁調(diào)用,所以錯誤處理時開銷稍大一點基本上不是什么問題。這也是我們提倡僅將異常用于錯誤處理的原因之一。
更多關(guān)于效率的討論,參見:RTTI、虛函數(shù)和虛基類的開銷分析和使用指導(dǎo)
本站僅提供存儲服務(wù),所有內(nèi)容均由用戶發(fā)布,如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請點擊舉報
打開APP,閱讀全文并永久保存 查看更多類似文章
猜你喜歡
類似文章
嵌入式程序設(shè)計的相關(guān)編寫規(guī)范
Google C 編程規(guī)范
C語言編程規(guī)范 clean code
C++編碼規(guī)范之代碼注釋
C++編程命名規(guī)則
c/c++語言規(guī)范
更多類似文章 >>
生活服務(wù)
熱點新聞
分享 收藏 導(dǎo)長圖 關(guān)注 下載文章
綁定賬號成功
后續(xù)可登錄賬號暢享VIP特權(quán)!
如果VIP功能使用有故障,
可點擊這里聯(lián)系客服!

聯(lián)系客服