【代码规范】google开源c\c++项目代码规范
#define FOO_BAR_BAZ_H_
…
#endif // FOO_BAR_BAZ_H_
1.3。前置聲明
TIP
盡可能地避免使用前置聲明。使用? #include?所有游戲需要的頭文件即可。
定義:
所謂「前置聲明」(forward declaration)是類,函數和模板的純粹聲明,沒伴隨著其定義。優點:
- 前置聲明能夠節省編譯時間,的多余? #include?會迫使compile-器展開更多的文件,處理更多的輸入。
- 前置聲明能夠節省不必要的重新編譯的時間。? #include?使代碼因為頭文件中無關的改動而被重新編譯多次。
缺點:
前置聲明隱藏了依賴關系,頭文件改動時,用戶的代碼會跳過必要的重新編譯過程。
前置聲明可能會被庫的后續更改所破壞。前置聲明函數或模板有時會妨礙頭文件開發者變動其API。例如擴大形參類型,加個自帶默認參數的模板形參等等。
前置聲明來自命名空間? std::?的符號時,其行為未定義。
很難判斷什么時候該用前置聲明,時候什么用該? #include?極端情況下,用前置聲明代替。? includes?甚至都會暗暗地改變代碼的含義:
// bh:
struct B {};
struct D : B {}
// good_user.cc:
#包括 “BH”
void f (B * );
void f (void * );
void test (D * x ) { f (x ); } //調用f(B )
- 前置聲明了include?不少來自頭文件的符號時,就會比單單一行的? 冗長。
- 僅僅為了能前置聲明而重構代碼(比如用指針成員代替對象成員)會使代碼變得更慢更復雜。
結論:
- 盡量避免前置聲明那些定義在其他項目中的實體。
- 函數:總是使用? #include。
- 類模板:優先使用? #include。
至于什么時候包含頭文件,參見? 1.5。#include的路徑及順序 ?。
1.4。內聯函數
TIP
只有當函數只有10行甚至更少時才將其定義為內聯函數。
定義:
當函數被聲明為內聯函數之后,編譯器會將其內聯展開,而不是按通常的函數調用機制進行調用。優點:
只要內聯的函數體小小,內聯該函數可以令目標代碼更加高效。對于存取函數以及其它函數體比較短,性??能關鍵的函數,鼓勵使用內聯。缺點:
濫用內聯將導致程序變得更慢。內聯可能使目標代碼量或增或減,這取決于內聯函數的大小。內聯非常短小的存取函數通常會減少代碼大小,但內聯一個相當大的函數將戲劇性的增加代碼大小。現代處理器由于更好的利用了指令緩存,小巧的代碼往往執行更快。結論:
一個較為合理的經驗準則是,不要內聯超過10行的函數。謹謹對待析構函數,析構函數往往比其表面看起來要更長,因為有隱含的成員和基類析構函數被調用!
另一個實用的經驗準則:內聯那些包含循環或? switch?語句的函數常常是得不償失(除非在大多數情況下,這些循環或? switch?語句從不被執行)。
有些函數即使聲明為內聯的也不一定會被編譯器內聯,這點很重要; 比如虛函數和遞歸函數就不會被正常內聯。通常,遞歸函數不應該聲明成內聯函數。(YuleFox注:遞歸調用堆棧的展開并不像循環那么簡單,比如遞進層數在編譯時可能是未知的,大多數編譯器都不支持內聯遞歸函數)。虛函數內聯的主要原因是想把它的函數體放在類定義內,為了圖個方便,抑或是當作文件描述其行為,比如精短的存取函數。
1.5。? #include?的路徑及順序
TIP
使用標準的頭文件包含順序可增強可讀性,避免隱藏依賴:相關頭文件,C庫,C ++庫,其他庫的? .h,本項目內的? .h。
項目內部文件應按照項目源代碼目錄樹結構排列,避免使用UNIX特殊的快捷目錄? .(當前目錄)或? …?(上級目錄)。例如,? google-awesome-project/src/base/logging.h?應該按如下方式包含:
#include “base / logging.h”又如,? dir/foo.cc?或? dir/foo_test.cc?的主要作用英文的英文實現或測試? dir2/foo2.h?的功能,? foo.cc?中包含頭文件的次序如下:
優先這種順序的排序保證當? dir2/foo2.h?遺漏某些必要的庫時,? dir/foo.cc?或? dir/foo_test.cc?的構建會立刻中止。因此這一條規則保證維護這些文件的人們首先看到構建中止的消息而不是維護其他包的人們。
dir/foo.cc?和? dir2/foo2.h?通常位于同一目錄下(如? base/basictypes_unittest.cc?和? base/basictypes.h),但也可放在不同目錄下。
按字母順序分別對每種類型的頭文件進行二次排序是不錯的主意。注意較老的代碼可不符合這條規則,要在方便的時候改正它們。
您所依賴的符號(符號)被哪些頭文件所定義,您就應該包含(包括)哪些頭文件,前置聲明 ?(向前聲明)情況除外。您比如要用到? bar.h?中的某個符號,哪怕您所包含的? foo.h?已經包含了? bar.h,也照樣得包含? bar.h,除非foo.h?有明確? 說明它會自動向您提供? bar.h?中符號。不過,凡是cc文件所對應的「相關頭文件」已經包含的,就不用再重復包含進其cc文件里面了,就像? foo.cc?只包含? foo.h就夠了,不用再管后者所包含的其它內容。
舉#include “foo / public / fooserver.h”//優先位置
#include “foo / public / bar.h”例如,???google-awesome-project/src/foo/internal/fooserver.cc?包含次序如下:
4.函數
4.1。參數順序
總述
函數的參數順序為:輸入參數在先,后跟輸出參數。
說明
C / C ++中的函數參數或者是函數的輸入,或者是函數的輸出,或兼而有之。輸入參數通常是值參或? const?引用,輸出參數或輸入/輸出參數則一般為非? const?指針。在排列參數順序時,將所有的輸入參數置于輸出參數之前。特別要注意,在加入新參數時不要因為它們是新參數就置于參數列表最后,而是仍然要按照前述的規則,即將新的輸入參數也置于輸出參數之前。
這并非一個硬性規定。輸入/輸出參數(通常是類或結構體)讓這個問題變得復雜。并且,有時候為了其他函數保持一致,你可能不得不不所有變通。
4.2。編寫簡短函數
總述
我們傾向于編寫簡短,凝練的函數。
說明
我們承認長函數有時是合理的,因此并不硬限制函數的長度。如果函數超過40行,可以思索一下能不能在不影響程序結構的前提下對其進行分割。
即使一個長函數現在工作的非常好,一旦有人對其修改,有可能出現新的問題,甚至導致難以發現的錯誤。使函數盡量簡短,以便于他在他人閱讀和修改代碼。
在處理代碼時,你可能會發現復雜的長函數。不要害怕修改現有代碼:如果證實這些代碼使用/調試起來很困難,或者你只需??要使用其中的一小段代碼,考慮將其分割為更加簡短并易于管理的若干函數。
4.3。引用參數
總述
所有按引用傳遞的參數必須加上? const。
定義
在C語言中,如果函數需要修改變量的值,參數必須為指針,如? 。在C ++中,函數還可以聲明為引用參數:? 。int?foo(int?pval)int?foo(int?&val)
優點
引用定義參數可以防止出現? (pval)++?這樣丑陋的代碼。引用參數對于拷貝構造函數這樣的應用也是必需的。同時也更明確地不接受空指針。
缺點
容易引起誤解,因為引用在語法上是值變量卻擁有指針的語義。
結論
函數參數列表中,所有引用參數都必須是? const:
void Foo (const string &in , string out );事實上這在Google Code是一個硬性約定:輸入參數是值參或? const?引用,輸出參數為指針。輸入參數可以是? const?指針,但決不能是非? const?引用參數,除非特殊要求,比如? swap()。
有時候,在輸入形參中用針指? ?比? ?更明智。比如:const?Tconst?T&
- 可能會傳遞空指針。
- 函數要把指針或對地址的引用賦值給輸入形參。
總而言之,大多時候輸入形參往往是? 。用若? ?則說明輸入側另有處理。所以若要使用? ,則應給出相應的理由,否則會使讀者感到迷惑。const?T&const?T*const?T*
4.4。函數重載
總述
若要使用函數重載,則必須能讓讀者一看調用點就胸有成竹,而不用花心思猜測調用的重載函數到底是哪一種。這一規則也適用于構造函數。
定義
你可以編寫一個參數類型為? ?的函數,然后用另一個參數類型為? ?的函數對其進行重載:const?string&const?char*
class MyClass {public :
void Analyze (const string &text );
void 分析(const char * text , size_t textlen );
};
優點
通過重載參數不同的同名函數,可以令代碼更直觀。模板化代碼需要重載,這同時也能為使用者帶來便利。
缺點
如果函數單靠不同的參數類型而重載(acgtyrant注:這意味著參數數量不變),讀者就得十分熟悉C ++五花八門的匹配規則,以了解匹配過程具體到底如何。另外,如果派生類只重載了某個函數的部分變體,繼承語義就容易令人困惑。
結論
如果打算重載一個函數,可以試試改在函數名里加參數信息。例如,用? AppendString()和? AppendInt()?等,而不是一口氣重載多個? Append()。如果重載函數的目的是為了支持不同數量的同一類型參數,則優先考慮使用? std::vector?以便使用者可以用? 列表初始化指定參數。
4.5。缺省參數
總述
只允許在非虛函數中使用缺省參數,且必須保證缺省參數的值始終一致。參數缺省與? 函數重載 ?遵循同樣的規則。一般情況下建議使用函數重載,尤其是在缺省函數帶來的可讀性提升不能彌補下文中所提到的缺點的情況下。
優點
有些函數一般情況下使用默認參數,但有時需要又使用非默認的參數。缺省參數為這樣的情形提供了便利,使程序員不需要為了極少的例外情況編寫大量的函數。和函數重載相比,缺省參數的語法更簡潔明了,減少了大量的樣板代碼,也更好地區別了“必要參數”和“可選參數”。
缺點
缺省參數實際上是函數重載語義的另一種實現方式,因此所有? 不應當使用函數重載的理由 ?也都適用于缺省參數。
虛函數調用的缺省參數取決于目標對象的靜態類型,此時無法保證給定函數的所有重載聲明的都是同樣的缺省參數。
缺省參數是在每個調用點都要進行重新求值的,這會造成生成的代碼迅速膨脹。作為讀者,一般來說也更希望缺省的參數在聲明時就已經被固定了,而不是在每次調用時都可能會有不同的取值。
缺省參數會干擾函數指針,導致函數簽名與調用點的簽名不一致。而函數重載不會導致這樣的問題。
結論
對于虛函數,不允許使用缺省參數,因為在虛函數中缺省參數不一定能正常工作。如果在每個調用點缺省參數的值都有可能不同,在這種情況下缺省函數也不允許使用。(例如,不要寫像? ?這樣的代碼。)void?f(int?n?=?counter++);
在其他情況下,如果缺省參數對可讀性的提升遠遠超過了以上提及的缺點的話,可以使用缺省參數。如果仍有疑惑,就使用函數重載。
4.6。函數返回類型后置語法
總述
只有在常規寫法(返回類型前置)不便于書寫或不便于閱讀時使用返回類型后置語法。
定義
C ++現在允許兩種不同的函數聲明方式。以往的寫法是將返回類型置于函數名之前。例如:
int foo (int x );C ++ 11引入了這一新的形式。現在可以在函數名前使用? auto?關鍵字,在參數列表之后后置返回類型。例如:
auto foo (int x ) - > int ;后置返回類型為函數作用域。對于像? int?這樣簡單的類型,兩種寫法沒有區別。但對于復雜的情況,例如類域中的類型聲明或者以函數參數的形式書寫的類型,寫法的不同會造成區別。
優點
后置返回類型是顯式地指定? Lambda表達式 ?的返回值的唯一方式。某些情況下,編譯器可以自動推導出Lambda表達式的返回類型,但并不是在所有的情況下都能實現。即使編譯器能夠自動推導,顯式地指定返回類型也能讓讀者更明了。
有時在已經出現了的函數參數列表之后指定返回類型,能夠讓書寫更簡單,也更易讀,尤其是在返回類型依賴于模板參數時。例如:
template < class T , class U > auto add (T t , U u ) - > decltype (t + u );對比下面的例子:
template < class T , class U > decltype (declval < T &> () + declval < U &gt ;) add (T t , U u );缺點
后置返回類型相對來說是非常新的語法,而且在C和Java中都沒有相似的寫法,因此可能對讀者來說比較陌生。
在已有的代碼中有大量的函數聲明,你不可能把它們都用新的語法重寫一遍。因此實際的做法只能是使用舊的語法或者新舊混用。在這種情況下,只使用一種版本是相對來說更規整的形式。
結論
在大部分情況下,應當繼續使用以往的函數聲明寫法,即將返回類型置于函數名前。只有在必要的時候(如Lambda表達式)或者使用后置語法能夠簡化書寫并且提高易讀性的時候才使用新的返回類型后置語法。但是后一種情況一般來說是很少見的,大部分時候都出現在相當復雜的模板代碼中,而多數情況下不鼓勵寫這樣? 復雜的模板代碼。
7.命名約定
最重要的一致性規則是命名管理。命名的風格能讓我們在不需要去查找類型聲明的條件下快速地了解某個名字代表的含義:類型,變量,函數,常量,宏,等等,甚至。我們大腦中的模式匹配引擎非常依賴這些命名規則。
命名規則具有一定隨意性,但相比按個人喜好命名,一致性更重要,所以無論你認為它們是否重要,規則總歸是規則。
7.1。通用命名規則
總述
函數命名,變量命名,文件命名要有描述性; 少用縮寫。
說明
盡可能使用描述性的命名,別心疼空間,畢竟相比之下讓代碼易于新讀者理解更重要。不要用只有項目開發者能理解的縮寫,也不要通過砍掉幾個字母來縮寫單詞。
int price_count_reader ; //無縮寫int num_errors ; //“num”是一個常見的寫法
int num_dns_connections ; //人人都知道“DNS”是什么
int n ; //毫無意義。
int nerr ; //含糊不清的縮寫。
int n_comp_conns ; //含糊不清的縮寫。
int wgc_connections ; //只有貴團隊知道是什么意思
int pc_reader ; //“pc”有太多可能的解釋了。
int cstmr_id ; //刪減了若干字母。
注意,一些特定的廣為人知的縮寫是允許的,例如用? i?表示迭代變量和用? T?表示模板參數。
模板參數的命名應當遵循對應的分類:類型模板參數應當遵循? 類型命名 ?的規則,而非類型模板應當? 遵循變量命名 ?的規則。
7.2。文件命名
總述
文件名要全部小寫,可以包含下劃線()或連-字符(),依照項目的約定。如果沒有約定,那么“ ” 更好。
說明
可接受的文件命名示例:
- my_useful_class.cc
- my-useful-class.cc
- myusefulclass.cc
- myusefulclass_test.cc?//? _unittest?狀語從句:? _regtest?已棄用。
C ++文件要以? .cc?結尾,頭文件以? .h?結尾。專門插入文本的文件則以? .inc?結尾,參見? 頭文件自足。
不要使用已經存在于? /usr/include?下的文件名(Yang.Y注:即編譯器搜索系統頭文件的路徑),如? db.h。
通常應盡量讓文件名更加明確。? http_server_logs.h?就比? logs.h?要好。定義類時文件名一般成對出現,如? foo_bar.h?和? foo_bar.cc,對應于類? FooBar。
聯內必須函數放在? .h?文件中。如果內聯函數比較短,就直接放在? .h?中。
7.3。類型命名
總述
類型名稱的每個單詞首字母均大寫,不包含下劃線:? MyExcitingClass,? MyExcitingEnum。
說明
所有類型命名 - 類,結構體,類型定義(typedef),枚舉,類型模板參數 - 均使用相同約定,即以大寫字母開始,每個單詞首字母均大寫,不包含下劃線。例如:
//類和結構體類 UrlTable { …
class UrlTableTester { …
struct UrlTableProperties { …
//類型定義
typedef hash_map < UrlTableProperties * , string > PropertiesMap ;
//使用別名
使用 PropertiesMap = hash_map < UrlTableProperties * , string > ;
//枚舉
enum UrlTableErrors { …
7.4。變量命名
總述
變量(包括函數參數)和數據成員名一律小寫,單詞之間用下劃線連接。類的成員變量以下劃線結尾,但結構體的就不用,如:? a_local_variable,? a_struct_data_member,? a_class_data_member_。
說明
普通變量命名
舉例:
字符串 table_name ; //好 - 用下劃線。字符串 表名; //好 - 全小寫。
字符串 tableName ; //差 - 混合大小寫
類數據成員
不管是靜態的還是非靜態的,類數據成員都可以和普通變量一樣,但要接下劃線。
類 TableInfo {…
private :
string table_name_ ; //好 - 后加下劃線。
字符串 tablename_ ; //好。
靜態 池< TableInfo > * pool_ ; //好。
};
結構體變量
不管是靜態的還是非靜態的,結構體數據成員都可以和普通變量一樣,不用像類那樣接下劃線:
struct UrlTableProperties {string name ;
int num_entries ;
靜態 池< UrlTableProperties > * 池;
};
結構體與類的使用討論,參考? 結構體與類。
7.5。常量命名
總述
聲明為? constexpr?或? const?的變量,或在程序運行期間其值始始保持不變的,命名時以“k”開頭,大小寫混合。例如:
const int kDaysInAWeek = 7 ;說明
所有具有靜態存儲類型的變量(例如靜態變量或全局變量,參見? 存儲類型)都應當以此方式命名。對于其他存儲類型的變量,如自動變量等,這條規則是可選的。如果不采用這條規則,就按照一般的變量命名規則。
7.7。函數命名
總述
常規函數使用大小寫混合,取值和設值函數則要求與變量名匹配:? MyExcitingFunction(),? MyExcitingMethod(),? my_exciting_member_variable(),? set_my_exciting_member_variable()。
說明
一般來說,函數名的每個單詞首字母大寫(即“駝峰變量名”或“帕斯卡變量名”),沒有下劃線。對于首字母縮寫的單詞,更傾向于將它們視作一個單詞進行首字母大寫(例如,寫作? StartRpc()?而非? StartRPC())。
AddTableEntry ()DeleteUrl ()
OpenFileOrDie ()
(同樣的命名規則同時適用于類作用域和命名空間作用域的常量,因為它們是作為API的一部分暴露對外的,因此應當讓它們看起來像是一個函數,因為在這時,它們實際上是一個對象而非函數的這一事實對外不過是一個無關緊要的實現細節。)
取值和設值函數的命名與變量一致。一般來說它們的名稱與實際的成員變量對應,但并不強制要求。例如? ?與? 。int?count()void?set_count(int?count)
7.7。命名空間命名
總述
命名空間以小寫字母命名。最高級命名空間的名字取決于項目名稱。要注意避免嵌套命名空間的名字之間和常見的頂級命名空間的名字之間發生沖突。
頂級命名空間的名稱應當是項目名或者是該命名空間中的代碼所屬的團隊的名字。命名空間中的代碼,應當存放于和命名空間的名字匹配的文件夾或其子文件夾中。
注意? 不使用縮寫作為名稱 ?的規則同樣適用于命名空間。命名空間中的代碼極少需要涉及命名空間的名稱,因此沒有必要在命名空間中使用縮寫。
要避免嵌套的命名空間與常見的頂級命名空間發生名稱沖突。由于名稱查找規則的存在,命名空間之間的沖突完全有可能導致編譯失敗。尤其是,不要創建嵌套的? std?命名空間。建議使用更獨特的項目標識符(websearch::index,? websearch::index_util)而非常見的極易發生沖突的名稱(比如? websearch::util)。
對于? internal?命名空間,要當心加入到同一? internal?命名空間的代碼之間發生沖突(由于內部維護人員通常來自同一團隊,因此常有可能導致沖突)。在這種情況下,請使用文件名以使內部名稱獨一無二(例如對于? frobber.h,使用? websearch::index::frobber_internal)。
7.8。枚舉命名
總述
的枚舉命名應當狀語從句:? 常量 ?或? 宏 ?harmony和諧:? kEnumName?或是? ENUM_NAME。
說明
的單獨枚舉值應該優先采用? 常量 ?的命名方式。但? 宏 ?方式的命名也。可以接受。枚舉名? UrlTableErrors?(以及? AlternateUrlTableErrors)是類型,所以要用大小寫混合的方式。
enum UrlTableErrors {kOK = 0 ,
kErrorOutOfMemory ,
kErrorMalformedInput ,
};
枚舉 AlternateUrlTableErrors {
OK = 0 ,
OUT_OF_MEMORY = 1 ,
MALFORMED_INPUT = 2 ,
};
2009年1月之前,我們一直建議采用? 宏 ?的方式命名枚舉值。由于枚舉值和宏之間的命名沖突,直接導致了很多問題。由此,這里改為優先選擇常量風格的命名方式。新代碼應該盡可能優先使用常量風格。但是老代碼沒必要切換到常量風格,除非宏風格確實會產生編譯期問題。
7.9。宏命名
總述
你并不打算? 使用宏,對吧?如果你一定要用,像這樣命名:? MY_MACRO_THAT_SCARES_SMALL_CHILDREN。
說明
參考? 預處理宏 ; 通常? 不應該 ?使用宏。如果不得不使用,其命名像枚舉命名一樣全部大寫,使用下劃線:
#define ROUND(x)…#define PI_ROUNDED 3.0
7.10。命名規則的特例
總述
如果你命名的實體與已有C / C ++實體相似,可參考現有命名策略。
bigopen():函數名,參照? open()?的形式
uint:?typedef
bigpos:? struct?或? class,參照? pos?的形式
sparse_hash_map:STL型實體; 參照STL命名約定
LONGLONG_MAX:常量,如同?INT_MAX
8.注意
注釋雖然寫起來很痛苦,但對保證代碼可讀性至關重要。下面的規則描述了如何注釋以及在哪兒注釋。當然也要記住:注釋固然很重要,但最好的代碼應當本身就是文檔。有意義的類型名和變量名,要遠勝過要用注釋解釋的含糊不清的名字。
你寫的注釋是給代碼讀者看的,也就是下一個需要理解你的代碼的人。所以慷慨些吧,下一個讀者可能就是你!
8.1。注釋風格
總述
使用? //?或? ,統一就好。/?/
說明
//?或? ?都可以; 但? 更 ?常用。要在如何注釋及注釋風格上確保統一。/?///?
8.2。文件注釋
總述
在每一個文件開頭加入版權公告。
文件注釋描述了該文件的內容。如果一個文件只聲明,或實現或測試了一個對象,并且這個對象已經在它的聲明處進行了詳細的注釋,那么就沒有必要再加上文件注釋。除此之外的其他文件都需要文件注釋。
說明
法律公告和作者信息
每個文件都應該包含許可證引用。為項目選擇合適的許可證版本(比如,Apache 2.0,BSD,LGPL,GPL)
如果你對原始作者的文件做了重大修改,請考慮刪除原作者信息。
文件內容
如果一個? .h?文件聲明了多個概念,則文件注釋應當對文件的內容做一個大致的說明,同時說明各個概念之間的聯系。一個一到兩行的文件注釋就足夠了,對于每個概念的詳細文檔應當放在各個概念中,而不是文件注釋中。
不要在? .h?和? .cc?之間復制注釋,這樣的注釋偏離了注釋的實際意義。
8.3。類注釋
總述
每個類的定義都要附帶一份注釋,描述類的功能和用法,除非它的功能相當明顯。
//遍歷GargantuanTable的內容。//示例:
// GargantuanTableIterator * iter = table-> NewIterator();
// it for(iter-> Seek(“foo”);!iter-> done(); iter-> Next()){
// process(iter-> key(),iter-> value());
//}
//刪除它;
類 GargantuanTableIterator {
…
};
說明
類注釋應當為讀者理解如何使用與何時使用類提供足夠的信息,同時應當提醒讀者在正確使用此類時應當考慮的因素。如果類有任何同步前提,請用文檔說明。如果該類的實例可被多線程訪問,要特別注意文檔說明多線程環境下相關的規則和常量使用。
如果你想用一小段代碼演示這個類的基本用法或通常用法,放在類注釋里也非常合適。
如果類的聲明和定義分開了(例如分別放在了? .h?和? .cc?文件中),此時,描述類用法的注釋應當和接口定義放在一起,描述類的操作和實現的注釋應當和實現放在一起。
8.4。函數注釋
總述
函數聲明處的注釋描述函數功能; 定義處的注釋描述函數實現。
說明
函數聲明
基本上每個函數聲明處前都應當加上注釋,描述函數的功能和用途。只有在函數的功能簡單而明顯時才能省略這些注釋(例如,簡單的取值和設值函數)。注釋使用敘述式(“打開文件”)而非指令式(“打開文件”); 注釋只是為了描述函數,而不是命令函數做什么。通常,注釋不會描述函數如何工作。那是函數定義部分的事情。
函數聲明處注釋的內容:
- 函數的輸入輸出。
- 對類成員函數而言:函數調用期間對象是否需要保持引用參數,是否會釋放這些參數。
- 函數是否分配了必須由調用者釋放的空間。
- 參數是否可以為空指針。
- 是否存在函數使用上的性能隱患。
- 如果函數是可重入的,其同步提提是什么?
舉例如下:
//返回此表的迭代器。當迭代器完成時,它是
客戶端的責任//并且一旦
創建
迭代器的GargantuanTable對象被刪除,它就不能使用迭代器。//
//迭代器最初位于表的開始位置。
//
//此方法等同于:
// Iterator * iter = table-> NewIterator();
// iter-> Seek(“”);
//返回iter;
//如果您要立即尋找到
返回的迭代器
中的其他位置,則使用NewIterator()會更快,并避免額外的查找。
Iterator * GetIterator () const;
但也要避免羅羅嗦嗦,或者對顯著易見的內容進行說明。下面的注釋就沒有必要加上“否則返回false”,因為已經暗含其中了:
//如果表不能包含更多條目,則返回true。bool IsTableFull ();
注釋函數重載時,注釋的重點應該是函數中被重載的部分,而不是簡單的重復被重載的函數的注釋。多數情況下,函數重載不需要額外的文檔,因此也沒有必要加上注釋。
注釋構造/析構函數,切記讀代碼的人知道構造/析構函數的所有功能,所以“銷毀這一對象”這樣的注釋是沒有意義的。你應該注意的是注意構造函數對參數做了什么(例如,是否取得指針所有權)以及析構函數清理了什么。如果都是些無關緊要的內容,直接省掉注釋。析構函數前沒有注釋是很正常的。
函數定義
如果函數的實現過程中用到了很巧妙的方式,那么在函數定義處應當加上解釋性的注釋。例如,你所使用的編程技巧,實現的大致步驟,或解釋如此實現的理由。舉個例子,你可以說明為什么函數的前半部分要加鎖而后半部分不需要。
不要 ?從? .h?文件或其他地方的函數聲明處直接復制注釋。簡要重述函數功能是可以的,但注釋重點要放在如何實現上。
8.5。變量注釋
總述
通常變量名本身足以很好說明變量用途。某些情況下,也需要額外的注釋說明。
說明
類數據成員
每個類數據成員(也叫實例變量或成員變量)都應該用注釋說明用途。如果有非變量的參數(例如特殊值,數據成員之間的關系,生命周期等)不能夠使用類型與變量名明確表達,則應當加上注釋。然而,如果變量類型與變量名已經足夠描述一個變量,那么就不需要加上注釋。
特別地,如果變量可以接受? NULL?或? -1?等警戒值,須加以說明。比如:
private ://用于限制檢查表訪問。-1意味著
//我們還不知道表中有多少個條目。
int num_total_entries_ ;
全局變量
和數據成員一樣,所有全局變量也要注釋說明含義及用途,以及作為全局變量的原因。比如:
//在此回歸測試中我們經歷的測試用例的總數。const int kNumTestCases = 6 ;
8.6。實現注釋
總述
對于代碼中巧妙的,晦澀的,有趣的,重要的地方加以注釋。
說明
代碼前注釋
巧妙或復雜的代碼段前要加注釋。比如:
//將結果除以2,考慮到x//包含來自add的進位。
for (int i = 0 ; i < result - > size (); i ++ ) {
x = (x << 8 ) + (* result )[ i ];
(* 結果)[ i ] = x >> 1 ;
x &= 1 ;
}
行注釋
比較隱晦的地方要在行尾加入注釋。在行尾空兩格進行注釋。比如:
//如果我們有足夠的內存,也可以對數據部分進行mmap。mmap_budget = max < int64 > (0 , mmap_budget - index_ - > length ());
if (mmap_budget > = data_size_ && !MmapData (mmap_chunk_bytes , mlock ))
return ; //錯誤已經記錄。
注意,這里用了兩段注釋分別描述這段代碼的作用,并提示函數返回錯誤已經被記入日志。
如果你需要連續進行多行注釋,可以使之對齊獲得更好的可讀性:
DoSomething (); //在這里發表評論,以便評論排成一行。DoSomethingElseThatIsLonger (); //代碼和注釋之間有兩個空格。
{ //允許打開一個新的作用域時,在注釋之前的一個空格
// //因此注釋與下面的注釋和代碼一起排列。
DoSomethingElse (); //通常在行注釋之前有兩個空格。
}
std :: vector < string > list {
//支撐列表中的注釋描述下一個元素…
“First item” ,
// …并且應該適當地對齊。
“第二項” };
做一點事(); / 對于尾部塊注釋,一個空間可以。 /
函數參數注釋
如果函數參數的意義不明顯,考慮用下面的方式進行彌補:
- 如果參數是一個字面常量,并且這一常量在多處函數調用中被使用,用以推斷它們一致,你應該用一個常量名讓這個約定變得更明顯,并且保證這一約定不會被打破。
- 考慮更改函數的簽名,讓某個? bool?類型的參數變為? enum?類型,這樣可以讓這個參數的值表達其意義。
- 如果某個函數有多個配置選項,你可以考慮定義一個類或結構體以保存所有的選項,并傳入類或結構體的實例。這樣的方法有許多優點,例如這樣的選項可以在調用處用變量名引用,這樣就能清晰地表明其意義。同時也減少了函數參數的數量,使得函數調用更易讀也易寫。除此之外,以這樣的方式,如果你使用其他的選項,就無需對調用點進行更改。
- 用具名變量代替大段而復雜的嵌套表達式。
- 萬不得已時,才考慮在調用點用注釋闡明參數的意義。
比如下面的示例的對比:
//這些論據是什么?const DecimalNumber product = CalculateProduct (values , 7 , false , nullptr );
和
ProductOptions 選項;選項。set_precision_decimals (7 );
選項。set_use_cache (ProductOptions :: kDontUseCache );
const DecimalNumber product =
CalculateProduct (values , options , / * completion_callback = * / nullptr );
哪個更清晰一目了然。
不允許的行為
不要描述顯而易見的現象,? 永遠不要 ?用自然語言翻譯代碼作為注釋,除非即使對深入理解C ++的讀者來說代碼的行為都是不明顯的。要假設讀代碼的人C ++水平比你高,即便他/她可能不知道你的用意:
你所提供的注釋應當解釋代碼? 為什么 ?要這么做和代碼的目的,或者最好是讓代碼自文檔化。
比較這樣的注釋:
//在矢量中查找元素。< - 差:這太明顯了!自動 ITER = STD :: 找到(v 。開始(), v 。端(), 元素);
如果 (ITER =! v 。端()) {
過程(元件);
}
和這樣的注釋:
//處理“元素”,除非它已經被處理。自動 ITER = STD :: 找到(v 。開始(), v 。端(), 元素);
如果 (ITER =! v 。端()) {
過程(元件);
}
自文檔化的代碼根本就不需要注釋。上面例子中的注釋對下面的代碼來說就是毫無必要的:
if (!IsAlreadyProcessed (element )) {Process (element );
}
8.8。標點,拼寫和語法
總述
注意標點,拼寫和語法; 寫的好的注釋比差的要易讀的多。
說明
注釋的通常寫法是包含正確大小寫和結尾句號的完整敘述性語句。大多數情況下,完整的句子比句子片段可讀性更高。短一點的注釋,比如代碼行尾注釋,可以隨意點,但依然要注意風格的一致性。
雖然被別人指出該用分號時卻用了逗號多少有些尷尬,但清晰易讀的代碼還是很重要的。正確的標點,拼寫和語法對此會有很大幫助。
8.8。TODO注釋
總述
對那些臨時的,短期的解決方案,或已經夠好,但仍不完美的代碼使用? TODO?注釋。
TODO?注意要使用全大寫的字符串? TODO,在隨后的圓括號里寫上你的名字,郵件地址,bug ID,或其它身份標識和與這一? TODO?相關的問題。主要目的是讓添加注釋的人(也是可以請求提供更多細節的人)可根據規范的? TODO?格式進行查找。添加? TODO?注釋并不意味著你要自己來修正,因此當你加上帶有姓名的時候? TODO?,一般都是寫上自己的名字。
// TODO(kl@gmail.com):這里使用“*”作為連接運算符。// TODO(Zeke)將其改為使用關系。
// TODO(錯誤12345):刪除“最后訪問者”功能
如果加? TODO?是為了在“將來某一天做某事”,可以附上一個非常明確的時間“Fix by November 2005”),或者一個明確的事項(“所有客戶端都可以處理XML響應時刪除此代碼。”) 。
8.9。棄用注釋
總述
通過棄用注釋(DEPRECATED?評論)以標記某接口點已棄用。
您可以寫上包含全大寫的? DEPRECATED?注釋,以標記某接口為棄用狀態。注釋可以放在接口聲明前,或者同一行。
在DEPRECATED?一詞后,在? 括號中留下您的名字,郵箱地址以及其他身份標識。
棄用注釋應當包涵簡短而清晰的指引,以幫助其他人修復其調用點。在C ++中,你可以將一個棄用函數改造成一個內聯函數,這一函數將調用新的接口。
DEPRECATED?僅僅標記接口為并? 不允許大家不約而同地棄用,您還得親自主動修正調用點(callsites),或是找個幫手。
修正好的代碼應該不會再涉及棄用接口點了,著實改用新接口點。如果您不知從何下手,可以找標記棄用注釋的當事人一起商量。
9.格式
每個人都可能有自己的代碼風格和格式,但如果一個項目中的所有人都遵循同一風格的話,這個項目就能更順利地進行。每個人未必能同意下述的每一處格式規則,而且其中的不少規則需要一定時間的適應,但整個項目服從統一的編程風格是很重要的,只有這樣才能讓所有人輕松地閱讀和理解代碼。
為了幫助你正確的格式化代碼,我們寫了一個? emacs配置文件。
9.1。行長度
總述
每一行代碼字符數不超過80。
我們也認識到這條規則是有爭議的,但很多已有代碼都遵照這一規則,因此我們感覺一致性更重要。
優點
提倡該原則的人認為強迫他們調整編輯器窗口大小是很野蠻的行為。很多人同時并排開幾個代碼窗口,根本沒有多余的空間拉伸窗口。大家都把窗口最大尺寸加以限定,并且80列寬是傳統標準。那么為什么要改變呢?
缺點
反對該原則的人則認為更寬的代碼行更易閱讀。80列的限制是上個世紀60年代的大型機的古板缺陷; 現代設備具有更寬的顯示屏,可以很輕松地顯示更多代碼。
結論
80個字符是最大值。
如果無法在不傷害易讀性的條件下進行斷行,那么注釋行可以超過80個字符,這樣可以方便復制粘貼。例如,帶有命令示例或URL的行可以超過80個字符。
長所有游戲的路徑? #include?語句可以超出80列。
文件頭保護 ?可以無視該原則。
9.2。非ASCII字符
總述
盡量不使用非ASCII字符,使用時必須使用UTF-8編碼。
說明
即使是英文,也不應將用戶界面的文本硬編碼到源代碼中,因此非ASCII字符應當很少被用到。特殊情況下可以適當包含此類字符。例如,代碼分析外部數據文件時,可以適當硬編碼數據文件中作為分隔符的非ASCII字符串; 更常見的是(不需要本地化的)單元測試代碼可能包含非ASCII字符串。此類情況下,應使用UTF-8編碼,因為很多工具都可以理解和處理UTF-8編碼。
十六進制編碼也可以,能增強可讀性的情況下尤其鼓鼓 - 比如? “\xEF\xBB\xBF”,或者更簡潔地寫作? u8"\uFEFF",在Unicode中是? 零寬度無間斷 ?的間隔符號,如果不用十六進制直接放在UTF -8格式的源文件中,是看不到的。
(Yang.Y注:? “\xEF\xBB\xBF”?通常用作帶編碼標記的UTF-8)
使用? u8?前綴把帶? uXXXX?轉義序列的字符串字面值編碼成UTF-8。不要用在本身就帶UTF-8字符的字符串字面值上,因為如果編譯器不把源代碼識別成UTF-8,輸出就會出錯。
別用C ++ 11的? char16_t?和? char32_t,它們和UTF-8文本沒有關系,? wchar_t?同理,除非你寫的代碼要調用Windows API,后者廣泛使用了? wchar_t。
9.3。空格還是制表位
總述
<span style="font-family:‘Microsoft YaHei’
總結
以上是生活随笔為你收集整理的【代码规范】google开源c\c++项目代码规范的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 简单常用滤波算法C语言实现
- 下一篇: c++中的左移、右移运算