VC++ 編碼規範

1 前言

本編碼規範針對C++語言。制定本規範的目的:

提高代碼的健壯性,使代碼更安全、可靠;

提高代碼的可讀性,使代碼易於查看和維護。

本文檔分別對C++程序的格式、註釋、標識符命名、語句使用、函數、類、程序組織、公共變量等方面做出了要求。規範分爲兩個級別——規則和建議。規則級的規範要求開發人員必須要遵守,建議級的規範開發人員應儘量遵守。

各項目組在用C++語言或基於C++語言的開發工具開發項目時,要遵守本規範。

2 編碼規範正文

     以下是各條規範的具體內容。

2.1格式

     對代碼書寫格式的要求。

2.1.1 空行的使用

規範級別:規則

規則描述:

●在頭文件和實現文件中,各主要部分之間要用空行隔開。

所謂文件的主要部分,包括:序言性註釋、防止被重複包含部分(只在頭文件中)、# include部分、#define部分、類型聲明和定義部分、實現部分等等。

●在一個函數中,完成不同功能的部分,要用空行隔開。

理由:

段落分明,提高代碼的可讀性。

2.1.2 哪裏應該使用空格

規範級別:規則

規則描述:

●在使用賦值運算符、邏輯運算符、位運算符、算術運算符等二元操作符時,在其兩邊各加一個空格。

例: nCount = 2;而不是  nCount=2;

● 函數的各參數間要用“,”和一個空格隔開。

              例:void  GetDate(int x, int y);

而不是void  GetDate(int x,int y)或void  GetDate(int x ,int y)。

理由:

提高代碼的可讀性。

2.1.3 哪裏不應該使用空格

規範級別:規則

規則描述:

●不要在引用操作符前後使用空格,引用操作符指“.”和“->”,以及“[]”。

●不要在“::”前後使用空格。

●不要在一元操作符和其操作對象之間使用空格,一元操作符包括“++”、“--”“!”、“&”“*”等。

理由:

提高代碼的可讀性。

舉例:

// 不要象下面這樣寫代碼:

m_pFont -> CreateFont();

//應該寫成這樣

m_pFont->CreateFont();

2.1.4 縮進

規範級別:規則

規則描述:

對程序語句要按其邏輯進行水平縮進,以兩個空格爲單位,使同一邏輯層次上的代碼在列上對齊。

理由:

提高代碼的可讀性。

2.1.5 長語句的書寫格式

規範級別:規則

規則描述:

較長的語句要分成多行書寫。長表達式要在低優先級操作符處分新行,操作符放在新行之首,劃分出的新行要進行適當的縮進,縮進長度以兩個空格或Tab符長度爲單位。

理由:

提高代碼的可讀性。

舉例:

// 下面是一個處理的較爲合理的例子

nCount = Fun1(n1, n2, n3)

+ (nNumber1 * GetDate(n4, n5, n6)) * nNumber1;

2.1.6 清晰劃分控制語句的語句塊

規範級別:規則

規則描述:

控制語句(if , for , while , do...whule)的語句部分一定要用 ‘{ ’和‘ }’括起來(即使只有一條語句),並且‘{ ’和‘ }’應處在同一列上。

理由:

這樣做,能夠劃分出清晰的語句塊,使語句的歸屬明確,使代碼更加容易閱讀和修改。

舉例:

//不要象下面這樣寫代碼:

if (x == 0)

return;

else

while (x > min)

x--;

// 應該這樣寫

if (x == 0)

{

return;

}

else

{

while (x > min)

{

x--;

}

}

2.1.7 一行只寫一條語句

規範級別:規則

規則描述:

一行只寫一條程序語句。

理由:

提高代碼的可讀性。

舉例:

// 不要這樣寫

x = x0; y = y0;

while(IsOk(x)) {x++;}

// 應該這樣寫代碼

x = x0;

y = y0;

while(IsOk(x))

{

x++;

}

2.1.8 一次只聲明、定義一個變量

規範級別:規則

規則描述:

一次(一條聲明、定義語句)只聲明、定義一個變量。

理由:

提高代碼的可讀性。

舉例:

// 應該這樣寫

int width;

int length;

// 不要這樣寫

int width, length;

2.1.9 在表達式中使用括號

規範級別:建議

規則描述:

對於一個表達式,在一個二元、三元操作符操作的操作數的兩邊,應該放置“(”和“)”。

理由:

避免出現不明確的運算、賦值順序,提高代碼的可讀性。

舉例:

// 下面這行代碼:

result = fact / 100 * number + rem;

//最好寫成這樣

result = ((fact / 100) * number) + rem;

2.1.10將操作符“*”、“&”和類型寫在一起

規範級別:規則

規則描述:

在定義指針、引用變量時,將操作符“*”、“&”和類型寫在一起。

理由:

統一格式,提高代碼的可讀性。

舉例:

// 不要象下面這樣寫代碼:

 char  *s;

       //而應該寫成這樣

char*  s;

2.2註釋

這一部分對程序註釋提出了要求。

程序中的註釋是程序與日後的程序讀者之間通信的重要手段。良好的註釋能夠幫助讀者理解程序,爲後續階段進行測試和維護提供明確的指導。

下面是關於註釋的基本原則:

1.  註釋內容要清晰明瞭,含義準確,防止出現二義性。

2.  邊寫代碼邊註釋,修改代碼的同時修改相應的註釋,保證代碼與註釋的一致性。

2.2.1 對函數進行註釋

規範級別:規則

規則描述:

●在函數的聲明之前,要給出精練的註釋(不必牽扯太多的內部細節),讓使用者能夠快速獲得足夠的信息使用函數。格式不做具體要求。

●在函數的定義之前,要給出足夠的註釋。註釋格式要求如下:

/*************************************************************************

【函數名稱】       (必需)

【函數功能】       (必需)

【參數】           (必需。標明各參數是輸入參數還是輸出參數。)

【訪問變量】       (必需。列出該函數訪問的全局變量、成員變量。)

【返回值】         (必需。解釋返回值的意義。)

【使用情況】       (必需。調用其它函數的情況,被調用的情況)

【開發者及日期】   (必需)

【版本】           (必需)

【更改記錄】       (若有修改,則必需註明)

*************************************************************************/

理由:

提高代碼的可讀性。

2.2.2 對類進行註釋

規範級別:規則

規則描述:

● 在類的聲明之前,要給出足夠而精練的註釋。註釋格式要求如下:

/*************************************************************************

【類名】             (必需)

【功能】            (必需)

【接口說明】           (必需)

【開發者及日期】       (必需)

【版本】              (必需)

【版權信息】           (可選)

【更改記錄】          (若修改過則必需註明)

*************************************************************************/

理由:

提高代碼的可讀性。

2.2.3 對文件進行註釋

規範級別:規則

規則描述:

在頭文件、實現文件的首部,一定要有文件註釋,用來介紹文件內容。註釋格式要求如下:

/*************************************************************************

【文件名】                 (必需)

【功能模塊和目的】      (必需)

【開發者及日期】           (必需)

【版本】                  (必需)

【版權信息】               (必需)

【更改記錄】              (若修改過則必需註明)

*************************************************************************/

理由:

提高代碼的可讀性。

2.2.4 對每個空循環體要給出確認性註釋

規範級別:建議

規則描述:

        建議對每個空循環體給出確認性註釋。

理由:

        提示自己和別人,這是空循環體,並不是忘了。

舉例:

        while(g_bOpen == FALSE)

        {

           //空循環

         }

2.2.5 對多個case語句共用一個出口的情況給出確認性註釋

規範級別:建議

規則描述:

建議對多個case語句共用一個出口的情況給出確認性註釋。

理由:

        提示自己和別人,這幾個case語句確實是共用一個出口,並不是遺漏了。

舉例:

        switch(nNumber)

        {           

           case 1:

nCount++;

break;

           case 2:

           case 3:

nCount--;

break;       // 當nNumber等於2或3時,進行同樣的處理

default:

break;

         }

2.2.6 其它應該考慮進行註釋的地方

規範級別:建議

規則描述:

除上面說到的,對於以下情況,也應該考慮進行註釋:

l 變量的聲明、定義。通過註釋,解釋變量的意義、存取關係等;

    例如:

           int m_nNumber;  //記錄圖形個數。被SetDate( )、GetDate( )使用。

l 數據結構的聲明。通過註釋,解釋數據結構的意義、用途等;

    例如:

           //定義結構體,存儲元件的端點。用於將新舊的端點對應。

typedef struct

{

                int nBNN;

                int nENN;

                int nBNO;

                int nENO;

}Element;

l分支。通過註釋,解釋不同分支的意義;

    例如:

                if(m_iShortRadio == 0)       //三相的情況

                {

                    strvC.Format("%-10.6f", vC);

                    straC.Format("%-10.6f", aC);

                }

                else if(m_iShortRadio == 1)    //兩相的情況

                {

                    strvC = _T("");

                    straC = _T("");

                    }

l調用函數。通過註釋,解釋調用該函數所要完成的功能;

    例如:

                  SetDate(m_nNumber );  //設置當前的圖形個數。

l 賦值。通過註釋,說明賦值的意義;

    例如:

           m_bDraw = true;  //將當前設置爲繪圖狀態

l 程序塊的結束處。通過註釋,標識程序塊的結束。

                       例如:

                           if (name = = White)

                           {

                                                …

if (age = = 20)

{

                                                              }//年齡判斷、處理結束

                   }//姓名判斷、處理結束

l其它有必要加以註釋的地方;

理由:

提高代碼的可讀性。

2.2.7 行末註釋儘量對齊

規範級別:建議

規則描述:

同一個函數或模塊中的行末註釋應儘量對齊。

理由:

提高代碼的可讀性。

舉例:

  nCount = 0;                   //計數器,表示正在處理第幾個數據塊

  BOOL bNeedSave;          //是否保存從服務器返回的數據

  BOOL bReturnCache;      //是否將Cache中的內容返回客戶端

    DWORD  BytesWritten;   //寫入的數據長度

2.2.8 註釋量

規範級別:規則

規則描述:

註釋行的數量不得少於程序行數量的1/3。

2.3命名

    對標識符和文件的命名要求。

2.3.1 標識符命名要求

規範級別:規則

規則描述:

在程序中聲明、定義的變量、常量、宏、類型、函數,在對其命名時應該遵守統一的命名規範。具體要求如下:

<!  l一般變量

一般變量名應以小寫字母打頭,各英文描述單詞的首字母分別大寫,其他字母一律小寫。對於不同作用域的變量,其命名要求如下表所示:

表2-1 變量命名

變量種類

前綴要求

示例

全局變量

g_

g_Number

全局指針變量

g_p

g_pNumber

對象級變量(類內數據成員)

文件作用域變量(文件中靜態變量)

m_

m_Number

對象級指針變量(類內指針數據成員)

文件作用域指針變量(文件中靜態指針變量)

m_p

m_pNumber

函數級變量(局部變量)

無要求

number

函數級指針變量(局部指針變量)

p

pNumber

    上表列出了對變量命名的基本要求。項目組或程序員可在該要求上再進行細化,但必須保證符合該要求。

<!  l常量

        常量的名字要全部大寫。常量指:

const修飾的量。如const int NUMBER = 100;

枚舉量。如enum{ ONE,TWO,THREE };

<!  l宏

        所有用宏形式定義的名字,包括宏常量和宏函數,名字要全部大寫。

<!  l類型

自定義類型名應以大寫字母打頭。C++中自定義類型包括:class、struct、enum、union、typedef聲明的類型、namespace。

例如:typedef struct Student;

      class CMsgDialog;

<!  l函數

函數名應以大寫字母打頭。

例如:void GetCount();
 

下面還有一些在命名時應該遵守的基本規範:

<!  l名中含多於一個單詞時,每個單詞的第一個字母大寫。

例如:m_LastCount 中要大寫L和C;

<!  l不要使用以下劃線“_”打頭的標識符。

    例如:_bFind 是不允許出現的變量;

<!  l不要使用僅用大小寫字母區分的名稱。

    例如:m_bFind 和 M_BFIND;

<!  l儘量使用有意義的名字。應做到見其名知其意。

例如:m_strError 表示錯誤的字符串;

理由:

減少命名衝突;提高代碼的可讀性。

2.3.2 標識符長度要求

規範級別:規則

規則描述:

在程序中聲明、定義的變量、常量、宏、類型、函數,它們的名字長度要在4至25個字符之內(下限不包括前綴,上限包括名字中所有的字符)。

對於某些已經被普遍認同的簡單命名,可不受本規則的限制。如for循環的循環記數變量,可使用 i 、j 等簡單字符命名。

理由:

名字長度應該在一個恰當的範圍內,名字太長不夠簡潔,名字太短又不能清晰表達含義。

2.3.3 文件命名要求

規範級別:建議

規則描述:

代碼文件的名字要與文件中聲明、定義的類的名字基本保持一致,使類名與類文件名建立聯繫。

理由:

使應用程序容易理解。

舉例:

將類CMsgDialog的頭文件和實現文件命名爲msgdialog.h和msgdialog.cpp就是一種比較簡單、恰當的方法。

2.4語句

    對具體程序語句的使用要求。

2.4.1 一條程序語句中只包含一個賦值操作符

規範級別:建議

規則描述:

在一條程序語句中,只應包含一個賦值操作符。賦值操作符包括:=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=,^=, ++, --。

理由:

避免產生不明確的賦值順序。

舉例:

// 不要這樣寫

b = c = 5;

a = (b * c) + d++;

// 應該這樣寫

c = 5;

b = c;

a = (b * c) + d;

d++;

2.4.2 不要在控制語句的條件表達式中使用賦值操作符

規範級別:建議

規則描述:

不要在控制語句if, while, for 和 switch的條件表達式中使用賦值操作符。賦值操作符包括:=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=,++,--。

理由:

一個類似於 if (x = y)這樣的寫法是不明確、不清晰的,代碼的作者也許是想寫成這樣: if (x == y)。

舉例:

//不要象下面這樣寫代碼:

if (x -= dx)

{

 ...

        }

//應該這樣寫:

x -= dx;

if (x)

{

...

}

2.4.3 賦值表達式中的規定

規範級別:建議

規則描述:

在一個賦值表達式中:

• 一個左值,在表達式中應該僅被賦值一次。

• 對於多重賦值表達式,一個左值在表達式中僅應出現一次,不要重複出現。

理由:

避免產生不明確的賦值順序。

舉例:

//不要象下面這樣寫代碼:

i = t[i++]; //一個左值,在表達式中應該僅被賦值一次

        a = b = c + a;    //對於多重賦值表達式,一個左值在表達式中僅應出現一次,不能重複出

現。

i = t[i] = 15;    //對於多重賦值表達式,一個左值在表達式中僅應出現一次,不能重複出現。

2.4.4 使用正規格式的布爾表達式

規範級別:建議

規則描述:

對於if, while, for等控制語句的條件表達式,建議使用正規的布爾格式。

理由:

使代碼更容易理解。

舉例:

//不要象下面這樣寫代碼:

while(1)

{

...

}

   

if(test)

{

...

}

for(i = 1; function_call(i); i++)

{

...

}

//最好這樣寫:

AlwaysTrue = true;

while(AlwaysTrue == true)

{

...

}

if(test == true)

{

...

}

for(i = 1; function_call(i) == true; i++)

{

...

}

2.4.5 禁用Goto語句

規範級別:規則

規則描述:

程序中不要使用goto語句。

理由:

這條規則的目的是爲了確保程序的結構化,因爲濫用goto語句會使程序流程無規則,可讀性差。

Goto語句只在一種情況下有使用價值,就是當要從多重循環深處跳轉到循環之外時,效率很高,但對於一般要求的軟件,沒有必要費勁心思追求多麼高的效率,而且效率主要是取決於算法,而不在於個別的語句技巧。

2.4.6 程序中禁用break、continue

規範級別:規則

規則描述:

在控制語句 (for, do, while) 塊中,禁止使用Break和continue。

在switch中的case語句塊不受該規則限制。

理由:

在控制語句 (for, do, while) 塊中使用Break和continue,會打亂代碼結構化的流程,使代碼的可讀性降低。

2.4.7 字符串的賦值

規範級別:規則

規則描述:

字符串的賦值應採用_T(“”)模式。

理由:

改善可移植性。

舉例:

//不要象下面這樣寫代碼:

        Cstring strError = “syntax error”;

//應該這樣寫:

Cstring strError = _T(“syntax error”);

2.4.8 避免對浮點數值類型做精確比較

規範級別:規則

規則描述:

不要對浮點類型的數據做等於、不等於這些精確的比較判斷,要用範圍比較代替精確比較。

理由:

由於存在舍入的問題,計算機內部不能精確的表示所有的十進制浮點數,用等於、不等於這種精確的比較方法就可能得出與預期相反的結果。所以應該用大於、小於等範圍比較的方法代替精確比較的方法。

舉例:

//不要象下面這樣寫代碼:

float number;

… …

        if (number = = 0)              //精確比較

… …

2.4.9 new 和 delete

規範級別:規則

規則描述:

局部的new 和 delete 要成對出現;

new要與delete對應,new[]要與delete[]對應。

理由:

防止內存泄露。

2.4.10對switch語句中每個分支結尾的要求

規範級別:規則

規則描述:

switch語句中的每一個case分支,都要以break作爲分支的結尾(幾個連續的空case語句允許共用一個)。

理由:

使代碼更容易理解;減少代碼發生錯誤的可能性。

2.4.11switch語句中的default分支

規範級別:規則

規則描述:

在switch語句塊中,一定要有default分支來處理其它情況。

理由:

用來處理switch語句中默認、特殊的情況。

2.4.12對指針的初始化

規範級別:建議

規則描述:

在定義指針變量的同時,對其進行初始化。如果定義時還不能爲指針變量賦予有效值,則使其指向NULL。

理由:

減少使用未初始化指針變量的機率。

舉例:

// 不要這樣寫代碼

int* y ;

y = &x ;

...

// 應該這樣寫

int* y = &x;

...

2.4.13釋放內存後的指針變量

規範級別:建議

規則描述:

當指針變量所指的內存被釋放後,應該賦予指針一個合理的值。除非該指針變量本身將要消失這種情況下不必賦值,否則應賦予NULL。

理由:

保證指針變量在其生命週期的全過程都指向一個合理的值。

2.4.14指針指向的數據成員的訪問方式

規範級別:規則

規則描述:

在代碼中用ptr->fld的形式代替(*ptr).fld的形式。

2.5函數

對函數的要求。

2.5.1 明確函數功能

規範級別:規則

規則描述:

函數體代碼長度不得超過100行(不包括註釋)。

理由:

明確函數功能(一個函數僅完成一件事情),精確(而不是近似)地實現函數設計。

2.5.2 將重複使用的代碼編寫成函數

規範級別:建議

規則描述:

將重複使用的簡單操作編寫成函數。

理由:

對於重複使用的功能,雖然很簡單,也應以函數的形式來處理,這樣可以簡化代碼,使代碼更易於維護。

2.5.3 儘量保持函數只有唯一出口

規範級別:規則

規則描述:

應該儘量保證一個函數只有一個出口。

理由:

增加函數的可靠性。

2.5.4 函數聲明和定義的格式要求

規範級別:規則

規則描述:

在聲明和定義函數時,在函數參數列表中爲各參數指定類型和名稱。

理由:

提高代碼的可讀性,改善可移植性。

舉例:

// 不要象下面這樣寫代碼:

f(int, char*);        //函數聲明

f(a, b)               //函數定義

int a;

char* b

{

...

}

  

// 應該這樣寫:

f(int a, char* b);

f(int a, char* b)

{

...

}

2.5.5 爲函數指定返回值

規範級別:規則

規則描述:

要爲每一個函數指定它的返回值。如果函數沒有返回值,則要定義返回類型爲void。

理由:

提高代碼的可讀性;改善代碼的可移植性。

2.5.6 在函數調用語句中不要使用賦值操作符

規範級別:建議

規則描述:

函數調用語句中,在函數的參數列表中不要使用賦值操作符。賦值操作符包括=, +=, -=, *=, /=, %=, >>=, <<=, &=, |=, ^=,++,--。

理由:

避免產生不明確的賦值順序。

舉例:

// 不要象下面這樣寫代碼:

void fun1(int a);

void fun2(int b)

{

         fun1(++b);   //注意這裏!

}

2.5.7 保護可重入函數中的全局變量

規範級別:規則

規則描述:

編寫可重入函數時,若操作全局變量,則應加以保護。

舉例:

如果全局變量不加以保護,當多個線程調用此函數時,很可能使此變量變爲不可知狀態。

         例如:假設 Exam是int型全局變量,函數Squre_Exam返回Exam平方值,如下函數不具有可重入性。

               Unsigned int example (int para)

               {

                  unsigned int temp;

<!--

                  Exam = para;

                  temp = Square_Exam();

        

                 return temp;

             }

           此函數若被多個線程調用,  Exam 可能成爲未知的。

可改爲如下方式:

Unsigned int example (int para)

              {

                   unsigned int temp;

                 

[申請信號量操作]    

Exam = para;        

temp = Square_Exam();

                    [釋放信號量操作]     

                 

return temp;

    }

2.6類

對類的要求。

2.6.1 關於默認構造函數

規範級別:規則

規則描述:

爲每一個類顯示定義默認構造函數。

理由:

確保類的編寫者考慮在類對象初始化時,可能出現的各種情況。

舉例:

class CMyClass

{

CMyClass();

...

};

2.6.2 關於拷貝構造函數

規範級別:規則

規則描述:

當類中包含指針類型的數據成員時,必須顯示的定義拷貝構造函數。建議爲每個類都顯示定義拷貝構造函數。

理由:

確保類的編寫者考慮類對象在被拷貝時可能出現的各種情況。

舉例:

class CMyClass

{

...

CMyClass(CMyClass& object);

...

};

2.6.3 爲類重載“=”操作符

規範級別:規則

規則描述:

當類中包含指針類型的數據成員時,必須顯示重載“=”操作符。建議爲每個類都顯示重載“=”操作符。

理由:

確保類的編寫者考慮將一個該類對象賦值給另一個該類的對象時,可能出現的各種情況。

舉例:

// 應該這樣寫代碼

class CMyClass

{

...

operator = (const CMyClass& object);

...

};

2.6.4 關於析構函數

規範級別:規則

規則描述:

爲每一個類顯示的定義析構函數。

理由:

確保類的編寫者考慮類對象在析構時,可能出現的各種情況。

舉例:

class CMyClass

{

...

~CMyClass (CMyClass& object);

...

};

2.6.5 虛擬析構函數

該規則參考自《Effective C++》中的條款 14。

規範級別:規則

規則描述:

基類的析構函數一定要爲虛擬函數(virtual Destructor)。

理由:

保證類對象內存被釋放之前,基類和派生類的析構函數都被調用。

2.6.6 不要重新定義繼承來的非虛函數

規範級別:規則

規則描述:

在派生類中不要對基類中的非虛函數重新進行定義。如果確實需要在派生類中對該函數進行不同的定義,那麼應該在基類中將該函數聲明爲虛函數;

理由:

不要忘了,當通過一個指向對象的指針調用成員函數時,最終調用哪個函數取決於指針本身的類型,而不是指針當前所指向的對象。

2.6.7 用內聯函數代替宏函數

規範級別:建議

規則描述:

用內聯函數代替宏函數。

理由:

同宏函數相比,內聯函數不但具有宏函數的效率,而且使用起來更安全。

2.6.8 如果重載了操作符"new",也應該重載操作符 "delete"

該規則參考自《Effective C++》中的條款10。

規範級別:規則

規則描述:

如果你爲一個類重載了操作符new,那你也應該爲這個類重載操作符delete。

理由:

操作符new和操作符delete需要一起合作。

2.6.9 類數據成員的訪問控制

規範級別:規則

規則描述:

類對外的接口應該是完全功能化的,類中可以定義Public的成員函數,但不應該有Public的數據成員。

理由:

要想改變對象的當前狀態,應該通過它的成員函數來實現,而不應該通過直接設置它的數據成員這種方法。一個類的數據成員應該聲明爲private的,最起碼也應該是protected的。

2.6.10限制類繼承的層數

規範級別:建議

規則描述:

當繼承的層數超過5層時,問題就很嚴重了,需要有特別的理由和解釋。

理由:

●很深的繼承通常意味着未做通盤的考慮;

●會顯著降低效率;

●可以嘗試用類的組合代替過多的繼承;

●與此類似,同層類的個數也不能太多,否則應該考慮是否要增加一個父類,以便做某種程度上的新的抽象,從而減少同層類的個數。

2.6.11慎用多繼承

規範級別:建議

規則描述:

C++提供多繼承的機制。多繼承在描述某些事物時可能是非常有利的,甚至是必須的,但我們在使用多繼承的時,一定要慎重,在決定使用多繼承時,確實要有非常充分的理由。

理由:

多繼承會顯著增加代碼的複雜性,還會帶來潛在的混淆。比如在很多C++書籍中提到的菱形繼承問題,如下圖所示:

logiscope84.jpg
圖2-1 菱形繼承

A派生子類B、C,D多繼承於B、C。這種情況下,會導致類D的對象中有兩個類A子對象。

2.6.12考慮類的複用

規範級別:建議

規則描述:

類設計的同時,考慮類的可複用性。

2.7程序組織

對程序組織的要求。

2.7.1 一個頭文件中只聲明一個類

規範級別:規則

規則描述:

在一個頭文件中,只應該包含對一個類的聲明(嵌套類的情況除外)。頭文件是指以.h、.hh、.H、.hxx、.hpp爲後綴的文件。

理由:

提高代碼的可讀性。

2.7.2 一個源文件中只實現一個類

規範級別:規則

規則描述:

在一個源文件中定義的每一個函數,都應該屬於同一個類,即對一個類的實現描述要獨佔一個文件。源文件指以*.cc, *.cxx, *.cpp, *.C or *.c爲後綴的代碼文件。

理由:

提高代碼的可讀性。

2.7.3 頭文件中只包含聲明,不應包含定義

規範級別:規則

規則描述:

在頭文件中只包含聲明,不要包含全局變量和函數的定義。頭文件指以.h、 .hh、.H、 .hxx、.hpp爲後綴的代碼文件。

內聯函數的情況除外。

理由:

在頭文件中只應該包含各種聲明,而不應該包含具體的實現。

2.7.4 源文件中不要有類的聲明

規範級別:規則

規則描述:

在源文件中只應該包含對類的實現,不應該包含任何類的聲明。類聲明應該統一放到頭文件中去。源文件指以*.cc, *.cxx, *.cpp, *.C or *.c爲後綴的代碼文件。

理由:

提高代碼的可讀性。

2.7.5 可被包含的文件

規範級別:規則

規則描述:

只允許頭文件被包含到其它的代碼文件中去。

理由:

改善程序代碼的組織結構。

2.7.6 避免頭文件的重複包含

規範級別:規則

規則描述:

頭文件的格式應該類似於:

#ifndef <IDENT>

#define <IDENT>

...

...

#endif

或者

#if !defined (<IDENT>)

#define <IDENT>

...

...

#endif

上面的<IDENT>是一個標識字符串,要求該標識字符串必須唯一。建議使用該文件的答謝文件名。

理由:

避免對同一頭文件的重複包含。

舉例:

// 對於文件audit.h,它的文件結構應該爲:

#ifndef AUDIT_H

#define AUDIT_H

...

...

#endif

2.8 公共變量

對公共變量(全局變量)的要求。

2.8.1 嚴格限制公共變量的使用

規範級別:建議

規則描述:

在程序中要儘可能少的使用公共變量。在決定使用一個公共變量時,要仔細考慮,權衡得失。

理由:

公共變量會增大模塊間的耦合。

2.8.2 明確公共變量的定義

規範級別:規則

規則描述:

當你真的決定使用公共變量時,要仔細定義並明確公共變量的含義、作用、取值範圍、與其它變量間的關係。明確公共變量與操作此公共變量的函數之間的關係,如訪問、修改和創建等。

2.8.3 防止公共變量與局部變量重名

規範級別:規則

規則描述:

防止公共變量與局部變量重名。

2.9 其它

    下面這幾條要求,不適合合併到上面任何一類,所以單獨作爲一部分。

2.9.1 不要使用結構體

規範級別:建議

規則描述:

在C++中,不要再使用struct。

理由:

以符合面向對象的思想。

2.9.2 不要使用聯合體

規範級別:建議

規則描述:

在C++中,不要再使用union。

理由:

●由於union成員公用內存空間所以容易出錯,並且維護困難;

●使用union通常意味着非面向對象的方法。

2.9.3 用常量代替宏

規範級別:規則

規則描述:

使用const來定義常量,代替通過宏來定義常量的方法。

理由:

在不損失效率的同時,使用const常量比宏更加安全。

舉例:

//宏定義的方法

#define string "Hello world!"

#define value  3

//常量定義的方法可以代替宏,且要更好

const char* string = "Hello world!";

const int value = 3;

2.9.4 括號在宏中的使用

規範級別:規則

規則描述:

對於宏的展開部分,在宏的參數出現的地方要加括號“()”。

理由:

保證宏替換的安全,同時提高代碼的可讀性。

舉例:

// 不要這樣寫

#define GET_NAME(obj,ind) obj->name[ind]

// 應該這樣寫

#define GET_NAME(obj,ind) (obj)->name[ind]

2.9.5 儘量使用C++風格的類型轉換

該規則參考自《More Effective C++》中的條款2。

規範級別:建議

規則描述:

用C++提供的類型轉換操作符(static_cast,const_cast, dynamic_cast和reinterpret_cast)代替C風格的類型轉換符。

理由:

C風格的類型轉換符有兩個缺點:

1 允許你在任何類型之間進行轉換,即使在這些類型之間存在着巨大的不同。

2 在程序語句中難以識別。

2.9.6 將不再使用的代碼刪掉

規範級別:規則

規則描述:

將程序中不再用到的、註釋掉的代碼及時清除掉。

理由:

理由不用做太多的解釋了吧?沒有用的東西就應該清理掉。如果覺得這些代碼你可能以後會用到,可以備份到其它地方,而不要留在正式的版本里。

3 結束

以上就是我們要求C++程序遵守的規範的全部內容。

發佈了9 篇原創文章 · 獲贊 8 · 訪問量 6萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章