# [27] 編碼規范
## FAQs in section [18]:
* [27.1] 有哪些好的C++編碼規范?
* [27.2] 編碼規范是必需的嗎?有它就夠了么?
* [27.3] 我們機構應該根據以前用C的經驗來制定編碼規范么?
* [27.4] `<xxx>`和`<xxx.h>`這兩種頭文件有何不同?
* [27.5] 我應該在我的代碼中使用using namespace std么?
* [27.6] ?:操作符能夠寫出難以閱讀的代碼,它是邪惡的么?
* [27.7] 我應該把變量聲明放在函數體中間還是開頭?
* [27.8] 哪種源代碼文件名最好?foo.cpp?foo.C?foo.cc?
* [27.9] 哪種頭文件名最好?foo.H?foo.hh?foo.hpp?
* [27.10] C++有沒有一些像lint一樣的規范原則?
* [27.11] 為何人們對指針轉換和/或引用轉換如此擔憂?
* [27.12]這兩種標識符的名字:that_look_like_this和thatLookLikeThis,哪種更好?
* [27.13]從哪里可以找到一些編碼規范么?
* [27.14] 我應該用“不常見”的語法么?
## 27.1 有哪些好的C++編碼規范?
很高興你在這里找答案,而不僅僅是試圖建立自己的編碼規范。
但要注意在[comp.lang.c++](news:comp.lang.c++)上有一些人對這個話題非常敏感。幾乎所有的軟件工程師在某個時候,都曾經被一些人利用過,這些人把編碼規范當作是一種“權力游戲”。另外,有些不知道自己在說些什么的人也設定了一些C++編碼規范,因此當這些標準的制定者實際寫代碼時,標準往往就成了只是用來觀賞的東西。這些情況促使人們不相信編碼規范。
很明顯,問這個問題的人們是想要得到訓練,因此他們并_不_逃避他們在知識上的欠缺。但雖然如此,在[comp.lang.c++](news:comp.lang.c++)上發帖問這個問題常常會導致爭吵,而不是解決方案。
Sutter和Alexandrescu對這個問題有本非常好的書叫"C++ Coding Standards"(220頁, Addison-Wesley出版,2005,ISBN 0-321-11358-6)。里面提供了101條規則、指導和最佳時間。作者和編輯們提供了一些確切的材料,并且對結對審查團隊很有幫助。所有這些都使得此書更有價值。值得購買。
## 27.2 編碼規范是必需的嗎?有它就夠了么?
編碼規范不會把一個不懂OO的程序員變得懂OO了,這需要培訓和經驗。編碼規范的好處是,當大型機構協調不同群體的程序員時,有助于降低分化的產生。
但僅有編碼規范是不夠的。編碼規范減少了新人的自由度,使他們不用操心一些事情,這是好的。但僅有編碼規范是不夠的,還需要更多實用的指南。機構需要一種一致的設計和實現的_哲學_。例如,是使用強類型還是弱類型?在接口中使用引用還是指針?用stream I/O還是stdio?C++代碼能夠調用C代碼么?反過來可以么?抽象基類應該怎么使用?繼承是應該采用實現方法還是特化方法?應采用何種測試策略和審查策略?接口應該統一為每個數據成員提供get()和/或set()么?接口應該是從外向內設計還是從內向外設計?錯誤是應該用try/catch/throw處理還是用錯誤碼?等等。
需要有一份有關詳細_設計_的“偽標準”。我推薦一種三頭并進的方法來達到這種標準化程度:培訓、[指導](http://www.sunistudio.com/cppfaq/how-to-learn-cpp#[28.1])和庫。培訓能夠提供“強化的指示”,指導能夠讓OO在實際中得到應用而不僅僅是教過就沒事了。而高質量的C++類庫則是一種“長期的指示”。針對這三種“訓練”的商業市場正不斷擴大。經歷過這些困難的機構給出的意見非常統一:_購買現成的,不要試圖構建自己的_。購買庫、培訓、工具和咨詢。如果一個公司試圖提供自足的工具,同時又制作應用程序或系統,那么它將發現很難取得成功。
很少人會認為編碼規范是“理想的”,甚至都算不上“良好”。但在上述的機構中,編碼規范又是必要的。
以下條目給出了一些基本的約定和風格方面的指南。
## 27.3 我們機構應該根據以前用C的經驗來制定編碼規范么?
不要!
不管你用C的經驗多么豐富,不管你對C掌握的多么熟練,一名好的C程序員不一定就會是一名好的C++程序員。從C轉換到C++,并不只是學習一下C++中++部分的語法和語義。那些希望獲得OO好處的機構,如果不在“OO編程”中真正運用“OO”技術,那么他們就是在自欺欺人;他們的蠢行最終會在財報上表現出來。
應該由C++專家來打造C++的編碼規范。開始可以在[comp.lang.c++](news:comp.lang.c++)上問問題。尋找能夠幫助你避開陷阱的專家。購買程序庫,然后看“好”的庫能否通過你的編碼規范。在獲得足夠的C++經驗之前,_不要_自己制定編碼規范。沒有標準要好過一份糟糕的標準,因為不合適的“官方”標準會讓錯誤的做法一直存在。現在C++培訓和程序庫的市場正不斷壯大,可以從中吸取經驗。
還有:只要對某件事物有需求,就會增加出現偽專家的機會。要三思而后行。同時還要從過去的公司中尋求反饋,因為有嫻熟技術的人未必是一名好的溝通者。最后,選一名會教學生的專業人,注意這可不是有足夠語言/編程范式相關知識的全職教師。
## 27.4 `<xxx>`和`<xxx.h>`這兩種頭文件有何不同?
ISO C++標準的頭文件不包含`.h`后綴。標準委員會修改了以前的做法。C的頭文件和C++頭文件在細節上不同。
C++標準庫保證包含來自C語言的那18個標準頭文件。這些頭文件有兩種標準風格:`<cxxx>`和`<xxx.h>`(這里`xxx`是頭文件的基本文件名,例如`stdio`,`stdlib`等等)。這兩種風格的頭文件是一樣的,但有一點不同:`<cxxx>`風格的頭文件將所有聲明放在`std`名字空間中,而`<xxx.h>`除了將聲明放在`std`名字空間,同時還放在了全局名字空間。委員會這么做是為了已有的C代碼能夠被C++編譯器編譯。但`<xxx.h>`是已經過時了,雖然現在仍被標準接受,但可能在以后的標準中不再支持。(參見ISO C++標準的D.5節)。
C++標準庫還增加了32個C里面沒有直接對應的標準頭文件,例如`<iostream>`, `<string>`和`<new>`。你可能會在老的代碼中看到#include `<iostream.h>`這種寫法,因此一些編譯器廠商還提供這些`.h`版本的頭文件。但要注意,`.h`版本的頭文件可能與標準版本不一樣。如果一個程序里有些用`<iostream>`,有些用`<iostream.h>`,那這個程序可能無法正常運行。
在新項目中,應當用`<xxx>`,而不該用`<xxx.h>`。
當修改或擴展使用舊式頭文件名的代碼時,最好還是遵從那些代碼的做法,除非有很重要的理由換用標準頭文件(例如標準`<iostream>`提供了一些功能,而廠商的`<iostream.h>`里沒有)。如果想要使以后代碼符合標準,那么要確保在所有被鏈接起來的代碼中包括外部庫,里面所用的所有C++頭文件都修改了。
以上內容只對標準頭文件有影響。你自己的頭文件可以隨便怎么命名,參見[27.9]。
## 27.5 我應該在我的代碼中使用`using namespace std`么?
可能不該。
人們不喜歡一邊又一遍地鍵入`std::`。他們發現`using namespace std`能夠使編譯器看到任何`std`中的名字,即使名字前沒有被`std`限定也能看到。問題是這會使編譯器看到_**所有**_在`std`中的名字,包括那些你沒想到的名字。換句話說,這可能導致名字沖突和二義性。
例如,假設你的代碼需要計數,然后你定義了一個名為`count`的變量或函數。但`std`庫也使用`count`這個名字(這是一個`std`算法),這就可能導致二義性。
你看,名字空間的用處就是用來防止兩部分獨立開發的代碼產生名字沖突。`using`指令(描述`using namespace XYZ`的術語)實際上是把一個名字空間的內容全部引入到另外一個名字空間了,這就違背了名字空間的本意。`using`指令是為了使遺留的C++代碼容易遷移到名字空間上來,但至少在新的C++代碼中,不該大范圍這么做。
如果真的不想敲`std::`,可以使用`using`聲明,或使自己適應`std::`(不是辦法的辦法)
* **使用`using`聲明**,這可以引入特定的名字。例如,為了能夠在代碼中使用`count`同時還不必寫`std::`,可以在代碼中插入一行`using std::count`。這不大可能會帶來混亂或二義性,因為是顯式引入名字的。
```
?#include?<vector>
?#include?<iostream>
?void?f(const?std::vector<double>&?v)
?{
???using?std::cout;??//?←?using聲明允許直接使用cout,前面不必限定。
???cout?<<?"Values:";
???for?(std::vector<double>::const_iterator?p?=?v.begin();?p?!=?v.end();?++p)
?????cout?<<?'?'?<<?*p;
???cout?<<?'\n';
?}
```
* **讓自己適應`std::`**(不是辦法的辦法):
```
?#include?<vector>
?#include?<iostream>
?void?f(const?std::vector<double>&?v)
?{
???std::cout?<<?"Values:";
???for?(std::vector<double>::const_iterator?p?=?v.begin();?p?!=?v.end();?++p)
?????std::cout?<<?'?'?<<?*p;
???std::cout?<<?'\n';
?}
```
我個人覺得與其為每個不同的`std`名字決定是否使用`using`聲明、并且找到最適合放置這個聲明的地方,不如直接敲"`std::`",這樣還更快。但這兩種方法都不錯。記住你是一個團隊的一分子,所以要確保使用的方法和其它人保持一致。
## 27.6 `?:`操作符能夠寫出難以閱讀的代碼,它是邪惡的么?
不是。但和往常一樣,記住可讀性是最重要的事情之一。
有人覺得應避免使用`?:`運算符,因為和`if`語句相比,它有時會令人困惑。在很多情況下,`?:`常會使代碼更難讀懂(因此應該替換為`if`語句)。但有時用`?:`更清晰,因為這會強調到底在干什么事情,而不是強調那里有個`if`。
讓我們先來看一個很簡單的例子。假設你需要打印出一個函數調用的結果。這時你應該把真正的目的(打印結果)放在開頭,然后把函數調用放在后面,因為函數調用是相對次要的(直覺上大多數開發者認為一行開頭的內容是最重要的)。
```
//?更好(強調主要目的-打印):
?std::cout?<<?funct();
?//?不那么好(強調次要目的-函數調用):
?functAndPrintOn(std::cout);
```
現在我們把這個觀點擴展到`?:`上來。假設你的真正目的是要打印一些東西,但需要做一些額外操作來決定打印的內容。因為在概念上打印是更重要的事,所以我們傾向于把它放在開頭,而把用于判斷的邏輯放在后面。在下面的例子中,變量`n`代表消息發送者的數量;消息本身被打印到`std::cout`上:
```
?int?n?=?/*...*/;???//?發送者的數量
//?更好(強調主要目的-打印):
?std::cout?<<?"Please?get?back?to?"?<<?(n==1???"me"?:?"us")?<<?"?soon!\n";
?//?不那么好(強調次要目的-函數調用):
?std::cout?<<?"Please?get?back?to?";
?if?(n?==?1)
???std::cout?<<?"me";
?else
???std::cout?<<?"us";
?std::cout?<<?"?soon!\n";
```
已經說過了,通過不同組合使用`?:`、`&&`和`||`等運算符,可以寫出很過分且難以閱讀的代碼(“只寫代碼”)。例如
```
//?更好(意思很明顯):
?if?(f())
???g();
?//?不太好(更難理解):
?f()?&&?g();
```
我個人覺得這里明確寫出`if`會更清晰,因為這強調主要的事情(至于要做什么是根據`f()`的結果來決定的),而不是強調次要的事情(調用`f()`)。換句話說,在這里使用`if`是_恰當_的,原因和上面用`if`是_不恰當_的一樣:我們希望把主要事情放在在顯眼位置,次要事情放到次要位置。
不管怎樣,別忘了可讀性是最終目的(至少是目的之一)。你的目標_不_應是為了避免類似`?:`、`||`、`if`或者甚至是`goto`這樣的語法結構。如果你變成一個“唯標準論者”,那么你最終會另自己蒙羞,因為任何基于語法的規則總是存在反例。如果你是強調大的目標和指導原則(例如“主要的事情要放在顯眼位置”,或者“把重要事情放在開頭”,甚至是“讓你的代碼含義明顯容易閱讀”),那就好多了。
寫出來的代碼是要被其它人讀的,不是給編譯器看的。
## 27.7 我應該把變量聲明放在函數體中間還是開頭?
在第一次使用的附近聲明。
對象是在聲明時被初始化(構造)的。如果在函數中間才有足夠的信息來初始化一個對象,那就應該把聲明放在中間,以便對象可以正確初始化。不要現在開頭給對象初始化為一個“空值”,然后在其它地方給它實際“賦值”。這么做是為了運行時的效率。與其先把對象構造到一個錯誤狀態,然后再修正,不如開始就把對象構造正確,這樣速度更快。簡單的例子表明對像`String`這樣簡單的類,也會有350%的速度差別。具體的數值可能有所不同,而且整個系統的效率損失肯定小于350%,但的確_會有_效率損失。_不必要_的效率損失。
對這個問題一個常見的回應是:“我們要為對象中的每個數據提供一個`set()`成員函數,這樣構造對象的代價就被平攤開了。”這就不僅是損失效率了,因為還導致維護困難。為每個數據成員提供`set()`函數和`public`數據一樣糟糕:你把實現技術暴露給外部了。你唯一隱藏掉的是成員對象的物理_名字_,而具體的實現細節(比方說用了一個`List`、一個`String`和一個`float`),則還是給外面知道了。
底線是:局部變量應在靠近第一次使用的地方聲明。對C語言專家來說可能不太習慣,但新事物不一定就不好。
## 27.8 哪種源代碼文件名最好?`foo.cpp`?`foo.C`?`foo.cc`?
如果已經有一種命名約定了,那就繼續使用。否則,需要查一下所使用的編譯器接受哪種文件名。常用的是:`.cpp`, `.C`, `.cc`或`.cxx`(當然如果用`.C`的話,那么文件系統需要能夠區分大小寫,以便不會混淆`.C`和`.c`)。
我們已經使用`.cpp`做為C++源文件名的后綴了,我們也用`.C`。如果用`.C`,那么在把代碼移植到大小寫不敏感的文件系統時,需要告訴編譯器將`.c`文件做為C++源文件對待(例如IBM CSet++是用`-Tdp`選項,Zortech C++編譯器用`-cpp`,Borland C++編譯器用`-P`)。
關鍵在于這些文件擴展名中,并不存在說哪個比其它更好。我們通常根據客戶的要求來選擇(這些問題應當依據商業上的考量,而不是技術)。
## 27.9 哪種頭文件名最好?`foo.H`?`foo.hh`?`foo.hpp`?
如果已經有一種命名約定了,那就繼續使用。如果還沒有,而且不需要讓編輯器區分C和C++文件,那就用`.h`好了。否則,就按編輯器的要求來,例如`.H`、`lhh`或`.hpp`。
我們傾向于使用`.h`或`.hpp`做為C++頭文件的后綴名。
## 27.10 C++有沒有一些像`lint`一樣的規范原則?
有的。有些做法一般被認為是危險的。但沒有一個是總是“不好”的,因為最糟糕的做法有時也有用武之地。
* `class Fred`的賦值運算符應該將`*this`做為`Fred&`返回(允許將賦值運算串起來)
* 一個類如果有虛。
* 一個類如果有{析構函數、賦值運算符、拷貝構造函數}中的任何一個,一般也需要另外兩個。
* `class Fred`的拷貝構造函數和賦值運算符的參數應該用`const`來限定,即`Fred::Fred(const Fred&)`和`Fred& Fred::operator=(const Fred&)`
* 當在構造函數中初始化對象成員時,總是使用初始化列表,而不是用賦值。對于用戶定義的類來說,這兩種辦法在性能上可能會有很大差別(3倍!)
* 賦值運算符需要保證當對自身賦值時不做任何操作,否則可能會有麻煩。有時這要求做顯式的判斷。
* 重載運算符時,要遵守指導原則。例如,如果類里面重載了`+=`和`+`,那么`a += b`和`a = a + b`一般來說應該是做相同的操作。其它內建/基本的類型也是如此(例如`a += 1`和`++a`;`p[i]`和`*(p+i);`等等)。在編寫二元運算符時,可以使用`op=`這種形式來強制達到這個目的。例如:
```
?Fred?operator+?(const?Fred&?a,?const?Fred&?b)
?{
???Fred?ans?=?a;
???ans?+=?b;
???return?ans;
?}
```
用這種辦法,那些“構造性的”[譯注1]二元運算符就不必成為類的[友元](http://www.sunistudio.com/cppfaq/friends.html)了。但有時可以更高效地實現一些普通的操作(例如,如果`class Fred`是`std::string`類型,并且`+=`需要重新分配/拷貝字符串內存,那么最好在開始就能夠知道最終的長度)。
[譯注1]: 這里“構造性”的指二元運算符的結果是一個新構造的對象。
## 27.11 為何人們對指針轉換和/或引用轉換如此擔憂?
因為它們是邪惡的!(這說明在使用它們時需要很小心謹慎)。
不知為什么,程序員在轉換指針時不太注意。他們到處轉換指針類型,然后還奇怪為什么會出問題。最糟糕的是,當編譯器給出一條錯誤消息時,他們就添加一個類型轉換“讓編譯器閉嘴”,然后他們再“測試一下”看能否運行。如果你做了很多指針或引用的類型轉換,請繼續往下讀。
當你轉換指針類型和/或引用類型時,編譯器通常不會產生任何信息。指針類型轉換(和引用類型轉換)會使編譯器保持沉默。我把他們當作是一種錯誤信息的過濾器:編譯器_想要_抱怨,因為它發現你正在做蠢事,但同時也發現它不該抱怨因為你用了類型轉換,所以編譯器就把錯誤消息丟掉了。這就像用密封膠帶封住編譯器的嘴:它試圖告訴你一些重要事情,而你卻故意讓它閉嘴。
指針類型轉換告訴編譯器:“別想了,趕緊生成代碼;我很聰明,你太笨了;我很偉大,你很渺小;我知道我在做什么,所以就假裝這是匯編語言,然后生成代碼吧。”當你轉換類型時,編譯器就盲目地生成代碼-由你來控制(和負責)生成的結果。編譯器和語言會縮減(甚至是消除)你所能得到的保證。你只能靠自己了。
做個類比,雖然手拋鏈鋸玩完全合法,但這么做卻很蠢。如果出了問題,別向鏈鋸制造商抱怨-你做了他們沒有保證的事情。你只能靠自己。
為公平起見,語言的確在類型轉換時做了一些保證,至少是在一個有限的子集內有保證。例如,語言保證當從對象指針(指向一塊數據的指針,不是指向函數,也不是指向成員)轉換到void*,并且再轉換_原_數據類型時,是沒有問題的。但很多時候,你只能靠自己。)
## 27.12這兩種標識符的名字:`that_look_like_this`和`thatLookLikeThis`,哪種更好?
這個要看以前是怎么做的。如果你有Pascal或Smalltalk背景,那么會喜歡`youProbablySquashNamesTogether`。如果有Ada背景,那么會喜歡`You_Probably_Use_A_Large_Number_Of_Underscores`. 如果有微軟Windows背景,那么可能傾向于“匈牙利”命名法,即在標識符前面添加表示類型的前綴[譯注1]。對于Unix C背景的人來說,會喜歡用縮寫[譯注2]。
所以沒有普遍適用的標準。如果你在的項目團隊已經有一份命名規范了,就照上面說的做。如果硬要推翻重來,可能更多會帶來爭吵而不是解決問題。從商業角度來看,只有兩件事是重要的:代碼可讀性好,團隊中的每個成員都使用相同風格。
除此之外,差別很小。
還有,在使用平臺相關的代碼時,不要用一種完全不同的風格。例如,一種編碼風格在使用微軟的庫時可能看起來很自然,但在和UNIX庫一起使用時就會看起來很奇異。別這么做。為不同的平臺使用不同的風格。(為避免有人不仔細看,別給我發email詢問那些要移植到(或是用在)不同平臺上的通用代碼,因為這些代碼不是平臺相關的,所以剛才說的“為不同的平臺使用不同的風格”在這里并不適用。)
好吧,還有。真的。別跟自動生成的代碼(例如通過工具產生的代碼)過不去。一些人對編碼規范抱有一種宗教般的狂熱,他們試圖讓工具產生的代碼符合他們的風格。別這么做,即使工具產生的代碼風格不同,也別管它。記住錢和時間才重要?!?整個編碼規范目的就是為了省錢省時間。別把這個變成燒錢的陷阱。
[譯注1]:原文是jkuidsPrefix vndskaIdentifiers ncqWith ksldjfTheir nmdsadType
[譯注2]: 原文是abbr evthng n use vry srt idntfr nms. (AND THE FORTRN PRGMRS LIMIT EVRYTH TO SIX LETTRS.)
## 27.13從哪里可以找到一些編碼規范么?
有好幾個地方可以找到。
在我看來,[Sutter和Alexandrescu的"C++ Coding Standards"(220頁,Addison-Wesley出版,2005, ISBN 0-321-11358-6)](http://www.amazon.com/exec/obidos/ASIN/0321113586/)是最好的。 我有理由推薦此書,并且本書作者很能激發推薦者的熱情。所有人都大力推薦,以前我可沒見過這事。
這里有一些編碼規范,可以以此為起點來制定機構的編碼規范。(列表順序是隨機的)(有些已經過時了,有些可能非常糟糕。我不會推薦任何一種。使用者自己注意。)
* [`www.codingstandard.com/`](http://www.codingstandard.com/ "www.codingstandard.com/")
* [`cdfsga.fnal.gov/computing/coding_guidelines/CodingGuidelines.html`](http://cdfsga.fnal.gov/computing/coding_guidelines/CodingGuidelines.html "cdfsga.fnal.gov/computing/coding_guidelines/CodingGuidelines.html")
* [`www.nfra.nl/~seg/cppStdDoc.html`](http://www.nfra.nl/~seg/cppStdDoc.html "www.nfra.nl/~seg/cppStdDoc.html")
* [`www.cs.umd.edu/users/cml/resources/cstyle`](http://www.cs.umd.edu/users/cml/resources/cstyle "www.cs.umd.edu/users/cml/resources/cstyle")
* [`www.cs.rice.edu/~dwallach/CPlusPlusStyle.html`](http://www.cs.rice.edu/~dwallach/CPlusPlusStyle.html "www.cs.rice.edu/~dwallach/CPlusPlusStyle.html")
* [`cpptips.hyperformix.com/conventions/cppconventions_1.html`](http://cpptips.hyperformix.com/conventions/cppconventions_1.html "cpptips.hyperformix.com/conventions/cppconventions_1.html")
* [`www.objectmentor.com/resources/articles/naming.htm`](http://www.objectmentor.com/resources/articles/naming.htm "www.objectmentor.com/resources/articles/naming.htm")
* [`www.arcticlabs.com/codingstandards/`](http://www.arcticlabs.com/codingstandards/ "www.arcticlabs.com/codingstandards/")
* [`www.possibility.com/cpp/CppCodingStandard.html`](http://www.possibility.com/cpp/CppCodingStandard.html "www.possibility.com/cpp/CppCodingStandard.html")
* [`www.cs.umd.edu/users/cml/cstyle/Wildfire-C++Style.html`](http://www.cs.umd.edu/users/cml/cstyle/Wildfire-C++Style.html "www.cs.umd.edu/users/cml/cstyle/Wildfire-C++Style.html")
* [Industrial Strength C++](http://hem.passagen.se/erinyq/industrial/ "hem.passagen.se/erinyq/industrial/")
* Ellemtel的編碼規范在這里可以找到:
* [`membres.lycos.fr/pierret/cpp2.htm`](http://membres.lycos.fr/pierret/cpp2.htm "membres.lycos.fr/pierret/cpp2.htm")
* [`www.cs.umd.edu/users/cml/cstyle/Ellemtel-rules.html`](http://www.cs.umd.edu/users/cml/cstyle/Ellemtel-rules.html "www.cs.umd.edu/users/cml/cstyle/Ellemtel-rules.html")
* [`www.doc.ic.ac.uk/lab/cplus/c++.rules/`](http://www.doc.ic.ac.uk/lab/cplus/c++.rules/ "www.doc.ic.ac.uk/lab/cplus/c++.rules/")
* [`www.mgl.co.uk/people/kirit/cpprules.html`](http://www.mgl.co.uk/people/kirit/cpprules.html "www.mgl.co.uk/people/kirit/cpprules.html")
注意:
* Ellement的標準已經過時了,但鑒于其重要地位,所以仍然列出。它是第一個廣泛傳播并被采用的C++編碼規范,也是第一個批判使用保護成員的。
* Industrial Strength的C++規范也過時了,但在那些提到在基類中使用保護非虛析構函數的規范中,它是第一個被廣泛發行的。
## 27.14 我應該用“不常見”的語法么?
只有當有足夠的理由時再去用。換句話說,就是通過“普通”的語法無法獲得同樣的結果。
決定軟件方面決策的是錢。除非你是在象牙塔中,否則,當你的做法會增加費用、增加風險、增加時間,或者是在一個受限環境中增加產品的時空開銷,那么你的做法就不好。在意識中,你應該把這些都轉換為鈔票。
根據這種以實用為目的、以鈔票為導向的觀點,只要有等價的“正常”語法,程序員就應避免實用非主流的語法。如果一個程序員寫下隱晦的代碼,其它程序員看了會困惑,這就會耗費金錢。其它程序員可能會引入bug(會花錢),可能會需要更長的時間來維護(錢),修改起來可能會很困難(錯過了市場機遇等于損失了錢),可能在優化時更困難(在受限的環境中,有人會需要為更大內存、更快CPU和/或更大電池來買單),另外客戶可能還不滿意(錢)。這是一種有關風險和回報的權衡。但如果有等價的“正常”語法能夠達到同樣目的,那么再努力降低使用“非正常”語法所帶來的風險,就沒有任何“回報”。
例如,在[混亂C代碼大賽](http://www.ioccc.org/)中使用的技術,禮貌來講是不正常的。沒錯,其中很多是合法的,但不是所有合法的事情都合理。使用奇怪的技巧會使其它程序員感到困惑。一些程序員喜歡“秀”他們挑戰極限的能力,但這是把自我的虛榮心放在了比錢更重要的位置,是不專業的表現。坦白說,任何這么干的人都該被開除。(如果你覺得我太“刻薄”或“殘忍”,我建議你調整一下態度。記住:公司雇你來是為了來幫助它而不是來傷害它的。那些把自我放到公司最佳利益上的人應該被開除出去)。
舉個非主流語法的例子,`?:`運算符一般不作為語句來用。(一些人甚至不喜歡把它用在表達式里。但必須承認有_很多_地方用到了`?:`,所以不管喜歡不喜歡,(用作表達式)是“正常”的。這里有個把?:用作語句的例子:
```
?blah();
?blah();
?xyz()???foo()?:?bar();??//?應該用if/else
?blah();
?blah();
```
還有把`||`和`&&`當作"if-not"和"if"語句來用也是一樣道理。是的,Perl里面有這些慣用法,但C++不是Perl,用這些來代替`if`語句(而不是用在表達式中)在C++中是“不正常”的。例如:
```
?foo()?||?bar();??//?應該用if (!foo()) bar();
?foo()?&&?bar();??//?應該用if (foo()) bar();
```
這里還有個例子,好像是能夠運行,甚至是合法的,但絕不是正常的。
```
?void?f(const&?MyClass?x)??//?應該用const MyClass& x
?{
???...
?}
```
- C++ FAQ Lite
- [1] 復制許可
- [2] 在線站點分發本文檔
- [3] C++-FAQ-Book 與 C++-FAQ-Lite
- [6] 綜述
- [7] 類和對象
- [8] 引用
- [9] 內聯函數
- [10] 構造函數
- [11] 析構函數
- [12] 賦值算符
- [13] 運算符重載
- [14] 友元
- [15] 通過 &lt;iostream&gt; 和 &lt;cstdio&gt;輸入/輸出
- [16] 自由存儲(Freestore)管理
- [17] 異常和錯誤處理
- [18] const正確性
- [19] 繼承 — 基礎
- [20] 繼承 — 虛函數
- [21] 繼承 — 適當的繼承和可置換性
- [22] 繼承 — 抽象基類(ABCs)
- [23] 繼承 — 你所不知道的
- [24] 繼承 — 私有繼承和保護繼承
- [27] 編碼規范
- [28] 學習OO/C++
- [31] 引用與值的語義
- [32] 如何混合C和C++編程
- [33] 成員函數指針
- [35] 模板 ?
- [36] 序列化與反序列化
- [37] 類庫