天天看點

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

第一章

簡介

大型軟體項目通常由相應的大型開發團隊承擔。大型團隊生成的代碼要有項目範圍内可評測的品質,代碼必須遵從于某一标準并以此來評價。是以,對大型的項目團隊來說,建立一個程式設計的标準或一組指南很重要。

使用程式設計标準也使以下各項成為可能:

  • 增加開發過程代碼的強壯性、可讀性、易維護性;減少有經驗和無經驗開發人員程式設計所需的腦力工作;
  • 在項目範圍内統一代碼風格;
  • 通過人為以及自動的方式對最終軟體應用品質标準;
  • 使新的開發人員快速适應項目氛圍;
  • 支援項目資源的複用:允許開發人員從一個項目區域(或子項目團隊)移動到另一個,而不需要重新适應新的子項目團隊的氛圍。

本文的目的是表述 C++ 程式設計的規則、指南和提示(通常也稱之為指南),它們可用來作為程式設計标準的基礎。對工作在大型項目團隊的軟體工程師,這些都是需要的。

目前版本專注于程式的編制(雖然現在還很難在設計和程式設計間劃定明确的界限);以後将會添加設計指南。

指南包括了以下 C++ 開發的方面:

  • 如何組織項目代碼;
  • 程式設計風格(如何編寫實際的源代碼);
  • 如何記錄源代碼;
  • 代碼内名稱和源檔案所使用的命名約定;
  • 何時使用某些語言結構以及何時應避免某些語言結構。

它們從大量的行業知識中搜集而來。(請參見參考文獻:作者及參考文獻。)它們基于:

  • 廣為人知的軟體原理;
  • “好的”軟體實作;
  • 所學課程;
  • 主觀意願。

大多會基于幾個第一種類型,也會基于大量的第二種和第三種類型。不幸的是,某些也基于最後一種類型;這主要是因為程式設計是一個高度主觀的活動:程式設計沒有普遍接受的最好或正确的萬靈藥。

基本原則

清晰、可了解的 C++ 源代碼是規則和指南的主要目标:清晰、可了解的源代碼是軟體可靠性和可維護性的主要作用因素。清晰、可了解的代碼可以表示為以下三個簡單的基礎原理 [Kruchten, 94]。

最小混淆 - 它的生存期中,源代碼的讀遠比寫多,規約更是這樣。理想情況下,源代碼讀起來應該象英語一樣描述了所要做的事,這同時還帶來了它執行的好處。程式更多是為人編寫,而不是為計算機而編寫。閱讀代碼是一個複雜的腦力過程,它可由統一标準來簡化,在本文中還指最小混淆原則。整個項目中統一樣式是軟體開發團隊在程式設計标準上達成一緻的主要原因,它不應視為一種懲罰或對創造性和生産力的阻礙。

維護的唯一點 - 隻要可能,設計決策就應在源中隻表述一點,它的多數後果應程式化的派生于此點。不遵守這一原則嚴重損害了可維護性、可靠性和可了解性。

最小幹擾 - 最終,應用最小幹擾原則(它是易讀性的主要作用因素)。即,避免将源代碼與可視幹擾(如内容較少或對了解軟體目的不起作用的資訊)相混合:

指南這裡所要表達的精神是不要過于苛刻;而對正确安全的使用語言特性提供指導。優秀軟體的關鍵在于:

了解每一個特性以及它的限制和潛在的危險;

确切了解此特性可安全的使用于哪一個環境中;

做出使用高度可視特性的決定;

在合适的地方小心适度的使用特性。

前提

這裡的指南隻有少數幾個基本前提:

讀者了解 C++。

任何有益的地方都鼓勵使用 C++ 的進階特性,而不是隻允許使用一些程式員都不熟悉的低級特性。這是項目能從使用 C++ 中獲益的唯一方式。C++ 不應隻當成 C 來使用,事實上 C++ 的面向對象特性使它不會象 C 一樣使用。不鼓勵将代碼直譯成注釋;相反,在任何可能的地方應當使用源代碼代替注釋。

遵從大型項目的做法。

即使隻是為了項目一級或公司一級的實作和統一,大型項目中許多規則仍很有價值,它們在小型系統中也有使用。

代碼遵從于一個面向對象設計。

許多規則都會支援從面向對象 (OO) 概念到 C++ 特性和具體命名約定的系統映射。

指南分類

指南具有不同的重要性;其重要性由以下标準來衡量:

提示:

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

以上符号所确定的指南隻是一個小提示或小建議,即使忽略它也沒關系。

建議:

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

以上符号所确定的指南是一個建議,它通常是建立在更加技術性的基礎之上:封裝、内聚、耦合、可移植性或可複用性與某些實作中的性能一樣都會受到影響。除非有合适的理由,否則必須遵從建議。

要求或限制:

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

以上符号确定的一個指南就是一個要求或限制;不遵從它肯定會導緻壞的、不可靠的或不可移植的代碼。沒有棄權就不能違反要求或限制

最根本的原則

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用常識

當無法找到可用的規則或指南時;當規則明顯不适用時;或當其他都已失敗時:使用常識,并核查基本原則。這條規則勝過其它所有的規則。即使存在規則或指南時也需要常識。

第二章

代碼組織與風格

本章提供程式結構與層次的指導。

代碼結構

開發大型系統時通常将它分成許多小的功能子系統。子系統本身由許多代碼子產品組成。在 C++ 中,一個子產品通常包含了實作單一一個,少數情況下,也可能是一組緊密相關的抽象。C++ 中,一個抽象通常實作為一個類。一個類有兩個不同的構件:一個是對類客戶可見的接口,它提供了類能力和責任的聲明或規約;另一個是所聲明規約(類定義)的實作。

與類相似,子產品也有它的接口和實作:子產品接口包括對所包含子產品抽象(類聲明)的規約;子產品實作包括對抽象(類定義)的實際實作。

在系統構造時,可将子系統組織成協作的組或層來減少或控制它們間的依賴性。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不同檔案中放置子產品規約與實作

子產品的規約應放置在與子產品實施檔案不同的檔案中,此檔案指頭檔案。子產品的實施檔案可能放置于一個或多個實施檔案中。

如果子產品實作包括擴充内嵌函數、普通私有實作聲明、測試代碼或具體平台的代碼,那就把這些部分分開存儲,并以那部分内容來命名。

如果程式的可執行大小是一個要考慮的問題,則不常用到的函數應當置于它們各自的檔案中。

以以下方式構造一部分檔案名:

  • 以子產品的主要抽象名作為子產品名。
  • 為子產品名附加一部分類型名。選擇暗示它們類型的部分類型名。
  • 子產品名和部分名由分隔符分隔(如“_”(下劃線)或“.”(句點));選擇一個分隔符,使用它要前後一緻。
  • File_Name::= <Module_Name> [<Separator> <Part_Name>] '.'<File_Extension>

  • 為了更好的預測,對檔案名使用相同的大小寫,這與代碼内名稱的使用相同。

以下是子產品劃分與命名政策的示例:

  • module.inlines.cc - 如果一個子產品有多個潛在的内嵌函數,就将函數的定義置于一個單獨的

    内嵌

    檔案中(請參見“将子產品内嵌函數定義置于單獨檔案中”)。
  • module.private.hh - 如果子產品有許多常用的被其他部分引用的私有實作聲明,就把這些聲明分隔出去組成一個私有部分,它可由其他實施檔案包含。
  • module.private.cc - 子產品的私有實施函數定義,為編輯的友善将它分離出去。
  • module.function_name.cc - 如果可執行的大小是個要考慮的問題,應當分離出許多程式不需要的特殊成員函數,組成各自的實施檔案(請參見“如果程式大小需要考慮,将大型子產品分成多個變換單元”)。如果重載的函數置于不同檔案中,每一檔案函數名都應有一執行個體數字的字尾。如,function_name1 表示第一個獨立重載函數執行個體。
  • module.nested_class.cc - 子產品嵌套類的成員函數,置于其本身檔案中。
  • module.test.[hh/cc] - 如果一個子產品需要擴充測試代碼,則測試代碼必須在友測試類中予以聲明。友測試類應稱為

    Module.Test

    。将測試代碼聲明為友類有助于子產品及其測試代碼的獨立開發;這還允許測試代碼從最終子產品對象代碼中删除而源并不改變。
  • module.platform_name.cc - 分離出任意子產品的平台依賴性并以平台名稱命名部分名稱(請參見“分離平台依賴性”)。

選擇一個子產品劃分和命名的機制,使用它要前後一緻。

示例
SymaNetwork.hh         //包括類 “SymaNetwork”的聲明。      
SymaNetwork.Inlines.cc //内嵌定義子單元      
SymaNetwork.cc         //子產品的主要實施單元      
SymaNetwork.Private.cc //私有實施子單元      
SymaNetwork.Test.cc    //測試代碼子單元      
基本原理

從子產品的實施中分離出其規約有助于使用者和提供者代碼的獨立開發。

将一個子產品的實施分割成多個變換單元,這可以更好的支援删除對象代碼,并會導緻更小的可執行大小。

使用規範化的檔案命名與劃分約定允許在不檢查子產品的實際内容時了解其内容與組織。

将名稱從代碼讓渡給檔案名增加了可預測性,有助于建立基于檔案的工具而不需要複雜命名映射 [Ellemtel, 1993]。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
隻選擇一組檔案擴充名以區分頭檔案和實施檔案

常用的檔案擴充名是:對頭檔案,為

.h、.H、.hh、.hpp

.hxx

;對實施檔案,為.c、

.C、.cc、.cpp

和.

cxx

。選擇一組擴充名,使用它要前後一緻。

示例
SymaNetwork.hh  //擴充名“.hh”用來指定一個“SymaNetwork”子產品的頭檔案。      
SymaNetwork.cc  //擴充名“.cc”用來指定一個“SymaNetwork”子產品的實施檔案      
注意

對由名字空間封裝的頭檔案,C++ 草拟标準工作底稿也使用擴充名“.ns”。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
每個子產品規約避免定義多個類

隻有在極少數情況下多個類才能置于同一個子產品中;此時他們必須是緊密關聯關系的(如容器與它的疊代程式)如果所有類都需要對客戶子產品是可見的,可以把子產品的主要類及其支援類置于同一個頭檔案中。

基本原理

減少子產品的接口以及其他子產品對此接口的依賴。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免将私有實施聲明置于子產品的規約中

除了類的私有成員以外,子產品的私有實施聲明(如實施類型和支援類)不能出現在子產品的規約中。除非多個實施檔案都需要這些聲明,否則它們應當置于所需的實施檔案中;如果有多個實施檔案,則這些聲明應置于輔助私有頭檔案中。而其他實施檔案在需要時應包含這個輔助私有頭檔案。

這一做法保證了:

子產品規約清楚的表示了它的抽象,不再需要實施工件;

子產品規約越小越好,并是以減小了子產品間的編譯依賴(請參見“減小編譯依賴”);

示例
//子產品 foo 的規約,包含于檔案“foo.hh”中;//class foo{.. 聲明};//“foo.hh”結束;      
//子產品 foo 的私有聲明,包含于檔案“foo.private.hh”中;所有 foo 實施檔案都使用它。... 私有聲明      
//“foo.private.hh”結束;      
//子產品 foo 的實施,包含于以下多個檔案中// “foo.x.cc”和“foo.y.cc”      
//檔案“foo.x.cc”;      
//#include "foo.hh" //包含子產品本身的頭檔案      
#include "foo.private.hh" //包含實施//所需的聲明。... 定義      
//“foo.x.cc”結束      
//檔案“foo.y.cc”;      
//#include "foo.hh"#include "foo.private.hh"... 定義      
//“foo.y.cc”結束      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
通常使用

#include

來通路子產品的規約

子產品要使用另一子產品,則必須使用預處理

#include

指令來通路提供者子產品的規約。相應的,子產品不能再次聲明提供者子產品規約的任何一部分。

當包含檔案時,隻有對“标準”頭檔案使用

#include <header>

文法;對其餘的使用

#include

"header" 文法。

使用

#include

指令也适用于子產品自身的實施檔案:子產品實施檔案必須包括它本身的規約和私有輔助頭檔案(請參見“不同檔案中放置子產品規約與實施”);

示例
//子產品 foo 頭檔案中的規約      
// "foo.hh"//class foo{... 聲明};      
//“foo.hh”結束;      
//子產品 foo 在檔案“foo.cc”中的實施;      
//#include "foo.hh" // 實施檔案包含//它本身的規約... foo 成員的定義      
//“foo.cc”結束      

#include

規則的一個例外是當子產品隻通過引用(使用指針或引用類型聲明)使用或包含提供者子產品的類型(類);這種情況下引用或包含通過使用預先聲明來規約(請參見“減小編譯依賴”),而不使用

#include

指令。

隻包含絕對需要的檔案:這意味着子產品頭檔案不應包含隻有子產品實施需要的其他頭檔案。

示例
#include "a_supplier.hh"class needed_only_by_reference;//對隻需要指針或引用的類                              //使用                              //預先聲明來通路void operation_requiring_object(a_supplier required_supplier, ...);操作需要一個實際的提供者對象;//提供者規約必須已有 #include 指令。void some_operation(needed_only_by_reference& a_reference, ...);某些操作隻需要對對象進行引用;//是以為提供者使用預先聲明。      
基本原理

這一規則保證了:

  • 隻存在唯一的子產品接口聲明,所有的客戶隻看到同一接口;
  • 子產品間的編譯依賴最大程度的減小了;
  • 客戶不會為代碼無故産生不需要的編譯開支。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
将子產品内嵌函數的定義置于單獨的檔案中

當子產品有許多内嵌函數時,它們的定義應置于一個分離的隻有内嵌函數的檔案中。在子產品頭檔案的末尾應包含内嵌函數檔案。

請參見“使用

No_Inline

條件編譯符破壞内嵌編譯”。

基本原理

這種技術使實施細節不會聚集到子產品頭檔案中;是以,保留了一個清晰的規約。當不進内嵌編譯時,它也有助于減少代碼複制:使用條件編譯,可将内嵌函數編譯成一個對象檔案而不是靜态的編譯成每一個使用的子產品。相應的,内嵌函數定義不應在類定義中定義,除非它們非常瑣碎。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
如果程式規模是個要求考慮的問題,就把大的子產品分割成多個變換單元

将大型子產品分割成多個變換單元有助于在程式連結中删除未經引用的代碼。很少引用的成員函數應當分割成獨立檔案,與經常使用的函數相分離。最後,各成員函數可置于他們自己的檔案中 [Ellemtel, 1993]。

基本原理

連結器在對象檔案中消除無引用代碼的能力各不相同。将大型子產品分割成多個檔案允許這些連結器通過消除整個對象檔案中的連結來減少可執行大小 [Ellemtel, 1993]。

注意

先考慮子產品是否能分割成小的抽象,這也是很值得的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
分離平台依賴性

從獨立于平台的代碼中分離出依賴于平台的代碼;這有助于提高可移植性。依賴于平台的子產品應當具有受平台名限制的檔案名以突出對平台的依賴。

示例
SymaLowLevelStuff.hh         //“LowLevelStuff” 規約      
SymaLowLevelStuff.SunOS54.cc // SunOS 5.4 實施      
SymaLowLevelStuff.HPUX.cc    // HP-UX 實施      
SymaLowLevelStuff.AIX.cc     // AIX 實施      
注意

從構架與維護角度,将對平台的依賴包含在少數幾個低層子系統中也是一個好方法。

采納一個标準檔案内容結構,使用它要前後一緻。

建議檔案内容就構包括以下次序的以下部分:

  • 1. 重複包含保護(隻适用于規約)。

    2. 确定可選檔案與版本控制。

    3. 本單元所需包含的檔案。

    4. 子產品紀錄(隻适用于規約)。

    5. 聲明(類、類型、常量、對象、函數)和其他文本規約(前置及後置條件,常量)。

    6. 對這種子產品内嵌函數定義的包含。

    7. 定義(對象與函數)和私有實施聲明。

    8. 版權聲明。

    9. 可選版本控制曆史。

基本原理

以上檔案的内容次序,首先呈現給了客戶相關的資訊;這與安排類的公共、保護和私有部分次序的基本原理相一緻。

注意

根據公司的政策,版權資訊應置于檔案的首部。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
防止重複檔案包含的保護

通過在每個頭檔案中使用以下結構可以防止重複包含與編譯檔案:

#if !defined(module_name) //使用預處理符      
#define module_name       //防止重複        //包含...//聲明到此      
#include "module_name.inlines.cc" //可選的内嵌結構//包含到此。//包含子產品内嵌函數之後//沒有其他聲明。      
#endif       
//module_name.hh 結束      

對包含保護符使用子產品檔案名。對子產品名與操作符使用同一大小寫。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用"

No_Inline

"條件編譯符破壞内嵌編譯

使用以下條件編譯結構控制可内嵌函數的内嵌(相對于外部的)編譯。

//在檔案 module_name.inlines.hh 首部,      
#if !defined(module_name_inlines)      
#define module_name_inlines      
#if defined(No_Inline)      
#define inline //使内嵌關鍵詞無效      
#endif... //内嵌定義到此      
#endif       
//檔案 module_name.inlines.hh 結束      
//檔案 module_name.hh 尾//      
#if !defined(No_Inline)      
#include "module_name.inlines.hh"      
#endif      
//包含module_name.hh之後      
// module_name.cc 檔案首//      
#if defined(No_Inline)      
#include "module_name.inlines.hh"      
#endif      

條件編譯結構與多包含保護結構相似。如果未定義

No_Inline

,則内嵌函數與子產品規約一同編譯并自動從子產品實施中排除出去。如果定義了

No_Inline

,則子產品規約不包含内嵌定義,但廢除

inline

關鍵詞的子產品實施包含了它。

基本原理

以上技術允許内嵌函數在外部編譯時的精簡代碼複制。使用條件編譯,内嵌函數的簡單複制編譯成了定義子產品;而當由編譯器開關規約外部編譯時,所複制的代碼在每個使用的子產品裡編譯成“靜态”(内部連結)函數。

注意

條件編譯增加了維護建構依賴關系的複雜性。對這種複雜性的管理通常是利用把頭檔案和内嵌函數定義視為一個邏輯單元:實施檔案是以依賴于頭檔案以及内嵌函數定義檔案。

代碼風格

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
對嵌套語句使用小的、一緻的縮進風格

使用一緻縮進來清楚的界定嵌套語句;為達到這一目的,兩到四個空格的縮進是最有效的方式。我們推薦使用規則的兩空格縮進。

混合語句或塊語句的分界符 (

{}

),應當與周圍的語句處于縮進的同一級上(這意味着垂直布置

{}

)。通過選擇空格的數量對塊内的語句進行縮進。

switch

語句的 case 标号應當與

switch

語句處于同一縮進級;

switch

語句内語句應比

switch

語句本身和 case 标号多縮進一級。

示例
if (true){  		      
//新程式塊foo();       
//程式塊内語句       //縮進兩個空格      
}      
else{      
bar();      
}      
while (expression){statement();}      
switch (i){      
case 1:do_something();//相對 switch 語句本身   //縮進      
break;         //一層      
case 2://...      
default://...}                  
基本原理

能夠輕易識别程式塊,還要在不超出顯示屏或列印頁面界限的前提下有足夠多的嵌套,使用兩空格的縮進是一個折衷的方案。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
相對于函數名或作用域名縮進函數參數

如果一行之内難以容下函數聲明,則将函數的第一個參數放在函數名所在的那一行;其餘參數每個占一新行,使用與第一個參數相同的縮進。下面所示的聲明與縮進的風格,在函數傳回類型和名稱下留有空格;是以,提高了它們的可見性。

示例
void foo::function_decl( some_type first_parameter,       
some_other_type second_parameter,      
status_type and_subsequent);      

如果按照以上的指南會導緻換行,或參數縮進的太多,則以函數名或作用域名(類、名字空間)縮進所有的參數,每一參數獨占一行:

示例
void foo::function_with_a_long_name( //函數名是//不易看到的       
some_type first_parameter,      
some_other_type second_parameter,      
status_type and_subsequent);      
請參照下面的布置規則。      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用能夠适合标準列印紙大小的最大行寬

應限制程式行的寬度以防止在使用标準(信紙)或預設列印紙張進行列印時丢失資訊。

注意

如果縮進使深層的嵌套語句處于太右邊,語句又太長以緻超出了右邊的空白,這時就應當考慮将代碼分成更小的更易管理的函數。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用一緻換行

當函數聲明、定義、調用或 enum 聲明中枚舉操作符的參數清單不能置于一行,則将每一個清單元素置于單獨的一行(請參見“相對函數名和作用域縮進函數參數”)。

示例
enum color { red, orange, yellow, green,                   
//...violet      
};                  

如果一個類或函數模闆聲明太長,則在模闆的實參清單後進行連續換行。例如(标準疊代程式庫的聲明,[X3J16, 95]):

template <class InputIterator, class Distance>      
void advance(InputIterator& i, Distance n);      

第三章

注釋

本章對代碼注釋的使用提供指導。

注釋應當作為源代碼的補充,而不是直譯源代碼:

  • 它們應當幫助讀者掌握背景中的概念、依賴性、特别是複雜的資料代碼和算法。
  •   它們應當解釋不能直接從源代碼看出東西;它們不應複制語言的文法或語義。
  • 它們應當突出:與代碼或設計标準的不同點、受限特性的使用、以及特殊的“技巧”。

對每一行注釋,程式員都應能夠輕松回答:這一注釋有何價值?通常,合理選擇名稱就可不要注釋。除非注釋出現在正規的程式設計語言中 (PDL),否則編譯器不對其進行編譯;是以,根據單點維護原則,應當以源代碼的方式表達設計決策,而不是以注釋的方式,即使這樣需要更多的聲明。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用 C++ 風格的注釋而非 C 風格的注釋

應使用 C++ 風格注釋分界符"

//

",而非 C 風格的"

"。

基本原理

C++ 風格的注釋更易了解,它減少了由于偶然缺少一個注釋結束分隔符而造成大量代碼被注釋掉的風險。

反例
/*注釋開始,但缺少注釋結束分隔符      
do_something();      
do_something_else(); /*對 do_something_else 的注釋*/                                
//注釋到此結束。          // do_something 和      
// do_something_else       
//都意外的被注釋掉了!Do_further();      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
注釋與源代碼盡可能靠攏

注釋應緊貼它們所要注釋的代碼;它們使用相同的縮進,使用一個空注釋行接于代碼之後。

對多行連續語句的注釋應置于語句的上方作為語句的介紹性說明。而對單個語句的注釋應置于語句的下方。

示例
//置于語句前的注釋      
//它對下面多個語句進行注釋      
// ...void function();//      
//語句之後的注釋      
//它對前一個語句進行注釋。      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免行末注釋

注釋應避免與源結構處于同一行:否則會使注釋與其所注釋的源代碼不對齊。但在描述長聲明中的元素(如 enum 聲明中的枚舉操作符)時,也能容忍這種不好的注釋方式。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免注釋頭

避免使用包含作者、電話号碼、建立和修改日期的注釋頭:作者和電話号碼很快就過時了;而建立和修改日期以及修改的原因則最好由配置管理工具來維護(或其他形式的版本曆史檔案)。

即使對主結構(如函數和類),也要避免使用垂直滾動條,閉合的欄或框。它們搞亂了視覺效果,這樣就很難保持一緻性。使用空行而不是多個注釋行來分割相關的源代碼塊。使用一個空行來分離函數或類中的結構。使用兩個空行将函數與函數相分離。

架構或表單看起來具有較好的一緻性,還提醒了程式員來注釋代碼,但它們會導緻直譯的風格 [Kruchten, 94]。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用空注釋行分離注釋段

在一個注釋塊中,使用空注釋行而非空行來分割注釋段

示例
//在下一段中//需要繼續這裡的解釋空注釋行使得//同一注釋塊中//不同段間的界限分明。      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免備援

注釋中應避免重複程式辨別符,避免複制别處有的資訊(此時可使用一個指向資訊的指針)。否則程式中的任何一處改動都可能需要多處進行相應的變動。如果其他地方沒有進行所需的注釋改動,将會導緻誤注釋:這種結果比根本沒有注釋還要糟糕。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
編寫自記錄代碼而非注釋

時刻注意要編寫自記錄代碼而非注釋。這可通過選擇合理的名稱、使用特殊的臨時變量或重新安排代碼的結構來實施。注意注釋中的風格、文法和拼寫。使用自然語言注釋而不是電報或加密格式。

示例
替換如下代碼:do{...}       
while (string_utility.locate(ch, str) != 0); //當找到時退出查找循環。      
将以上代碼改寫成:do{...found_it = (string_utility.locate(ch, str) == 0);}       
while (!found_it);      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
記錄類與函數

雖然自記錄代碼比注釋好;但通常還需要提供一些超出解釋代碼複雜部分以外的資訊。至少需要記錄以下資訊:

  • 每個類的目的;
  • 函數名不能清楚說明它的目的時,則記錄函數相應的目的;
  • 傳回值的含義;如不可預測函數布爾傳回值的含義:如,ture 值是否表示函數執行成功;
  • 出現異常的條件;
  • 如果有的話,參數的前置或後置條件;
  • 其他資料通路,特别是當資料被修改時:對有副作用的函數特别重要;
  • 合理使用類或函數所需的限制或其他資訊;
  • 對類型和對象來說,語言無法表達的任何不變量或其他限制。
基本原理

與聲明相結合的代碼記錄應當足以使客戶使用代碼;因為隻使用 C++ 不能完全表達類、函數、類型和對象的完整語義,是以需要記錄存檔。

第四章

命名

本章為不同 C++ 實體命名指南。

概述

為程式實體(類、函數、類型、對象、常量、異常、名字空間)取一個好名稱并不是一件容易的事。對中大型的應用程式來說,問題就顯得更加困難:這裡名稱容易沖突,并且還缺少一些近義詞來區分相似但不相同的概念,這就更增加了困難的程度。

使用名稱約定可減少創造合适名稱時的腦力勞動。除了這一好處以外,名稱約定還帶來了代碼的一緻性。一個有用的名稱約定必須在以下方面提供向導:排版風格(或如何書寫名稱)以及名稱的建構(或如何選擇名稱)。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
選擇一個名稱約定,使用它要前後一緻

隻要能夠前後一緻,使用哪一個名稱約定并不重要。命名的一緻性比實際的約定重要的多:一緻性支援“最小混淆”原則。

因為 C++ 是一個區分大小寫的語言,并且在 C++ 應用界有多種不同的命名約定,幾乎不可能實作命名的絕對一緻。我們推薦基于主機的環境(如 UNIX 或 Windows)以及項目所使用的主要的庫選擇項目的命名約定;盡可能實作代碼的一緻性:

  • 主機使用 UNIX 且不常使用商業庫(如 X Window 庫、X Toolkit Intrinsics 和 Motif)的項目可能傾向于使用全部為小寫字元、以下劃線分隔的約定:這是 UNIX 系統調用以及 C++ 草拟标準工作底稿使用的約定。
  • 以商業庫為中心的 UNIX 主機項目可能更傾向于使用大寫風格,通常也稱為 Smalltalk 風格 - 一種首字母大寫,字間直接相連而無分隔符的書寫風格。
  • Microsoft 基于 Windows 的項目可能會推薦使用并不常用的 Microsoft® “匈牙利”命名法我們不推薦使用這種命名風格,因為它違背了本文指南的基本原則。
注意:

細心的讀者會發現本文的示例現在并未遵循所有的指南。這部分是因為示例的來源有多個,還因為希望減少篇幅。是以,沒有嚴格遵照格式化的指南來做。但需聲明:照我說的去做,不要學我的實際做法。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
永遠不要聲明以一個或多個下劃線 ('_') 開頭的名稱

開頭帶有一個下劃線(“_”)的名稱通常由庫函數(“

_main

”和“

_exit

”)使用。開頭帶有兩個下劃線(“__”)或一個下劃線後接一個大寫字母的名稱保留給編譯器内部使用。

名稱還要避免下劃線相鄰,否則很難識别下劃線的确切個數。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免使用隻靠字母大小寫才能區分的名稱

隻通過大小寫才能區分的類型名稱,它們間的差異是很難記憶的,也就很容易造成混淆。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免使用縮寫

應用領域中常用的縮寫(如 FFT 指快速傅立葉變換),或在項目縮寫清單中有定義的縮寫,才能使用相應的縮寫。否則,很有可能相似但不相同的縮寫到處出現,之後就引進了混淆和錯誤(如将 track_identification 縮寫成 trid、trck_id、tr_iden、tid、tr_ident等)。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免使用字尾來表明程式語言結構

使用字尾劃分實體種類(如對type(類型)使用 type,對 exceptions(異常)使用 error)對幫助了解代碼通常并不有效。字尾如 array 和 struct 還意味着一個具體的實作;在實作改變時(結構或數組表示的改變)對客戶代碼要麼會有反面的結果,要麼會起誤導的作用。

但字尾在有限幾種情況下也會有一定用處:

  • 當可供選擇的辨別符非常有限時,選擇一個最佳名稱并用字尾表明它的類型。
  • 當它表示一個應用領域的概念如 aircraft_type(飛行器類型)時。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
選擇清晰的、易辨認的、有意義的名稱

從應用的角度選擇名稱,名詞使用形容詞修飾來提高局部(具體上下文)的含義。確定名稱與其類型保持一緻。

選擇合适的名稱,使以下結構:

object_name.function_name(...);      
object_name->function_name(...);      

易于閱讀并有實際含義。

短名稱或縮寫名稱打字速度快不是使用它們的可以接受的理由。單字母或很短幾個字母的辨別符通常出于選擇不當或懶惰。但使用人們熟知的 E 作為自然對數的底數或 Pi 作為圓周率就是例外了。

不幸的是,編譯器和支援的工具有時限制名稱的長度。是以,應當注意:長名稱不應隻在結尾的幾個字元有所不同,因為結尾的幾個用于區分的字元可能會被這些工具截斷。

示例
void set_color(color new_color){      
...the_color = new_color;...}優于      
void set_foreground_color(color fg)和:oid set_foreground_color(color foreground);      
{...the_foreground_color = foreground;...}            

第一個示例中名稱的選擇要優于其它兩個:對

new_color

進行了限定使它與其類型一緻;是以增強了函數的語義。

第二個示例中,讀者直覺上會認為

fg

意指 foreground(前景);但是,任何一個好的程式設計風格都不應留給讀者作直覺推導的餘地。

第三個示例中,當使用參數

foreground

(遠離其聲明)時,讀者會認為

foreground

實際上就是指 foreground 的顔色。可以想象,它能代表任何一種可暗中轉化為

color

的類型。

注意:

使用名詞和形容詞構成名稱、確定名稱與類型相符、遵循自然語言以增強代碼的可讀性和語義。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用名稱的正确拼寫

英語字元名稱部分應正确的拼寫,遵守項目要求的形式,如使用一緻的英國英語或美國英語,但不能同時使用。這對注釋也同樣适用。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
布爾值使用正值謂詞從句

對布爾值類型的對象、函數及函數實參使用正值形式的判定句式,如

found_it

is_available

,但不使用

is_not_available

基本原理

當否定謂詞時,雙重否定更難以了解。

名字空間

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用名字空間由子系統或庫劃分潛在全局名稱

如果将系統解構成子系統,使用子系統名稱作為名字空間的名稱,這樣可劃分系統的全局名字空間并使其最小化。如果系統是一個庫,對整個庫使用一個最外層的名字空間。

為每一個子系統或庫名字空間取一個有意義的名稱;此外,為它取一個簡化的或縮寫的别名。選擇不會引起沖突的簡寫或縮寫的别名,如 ANSI C++ draft standard library(ANSI C++ 草拟标準庫)[Plauger, 95] 将

std

定義為

iso_standard_library

的别名。

如果編譯器尚未支援名字空間結構,使用名稱字首模拟名字空間。例如,系統管理子系統接口中的公共名稱應帶有字首 syma (System Management(系統管理)的簡寫)。

基本原理

使用名字空間包含可能的全局名稱,這樣有助于子項目團隊或廠商獨立開發代碼時避免名稱沖突。這必然導緻隻有名字空間是全局的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
類的名稱使用名詞或名詞短語

使用簡單形式的常用名詞或名詞短語,為類取名時要表達出它的抽象含義。基類使用更通用的名稱,而對派生類使用更專用的名稱。

typedef ... reference; //出自标準庫      
typedef ... pointer;   //出自标準庫      
typedef ... iterator;  //出自标準庫      
class bank_account {...};      
class savings_account : public bank_account {...};      
class checking_account : public bank_account {...};      

當對象和類型的名稱沖突或缺少合适的名稱時,對象使用簡單名稱,類型名稱添加字尾

mode、kind、code

等。

表明是對對象集合的抽象時使用複數形式。

typedef some_container<...>yellow_pages;      

當需要對象集合外的其他語義時,使用以下标準庫中的資料結構作為行為模式和名稱字尾:

  • vector(向量) - 一種可随機通路的順序容器;
  • list(表) - 一種有序的順序容器;
  • queue(隊列) - 一種先入先出的順序容器;
  • deque(雙端隊列) - 一種兩個端口都可進行操作的隊列;
  • stack(堆棧) - 一種後入先出的順序容器;
  • set(集合) - 一種關鍵詞通路(關聯關系)容器;
  • map(圖) - 一種關鍵詞通路(關聯關系)容器;

函數

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
過程類型的函數名稱使用動詞

對無傳回值的函數(函數聲明為 void 傳回類型)或傳回值使用指針或引用類型的函數,使用動詞或動詞短語。

對使用非 void 傳回類型傳回唯一一個值的函數,使用名詞或名詞短語。

對帶有常用操作(行為模式)的類,使用項目選項清單中的操作名。例如:begin, end, insert, erase (标準庫中的容器操作)。

避免使用“get”和“set”來命名表明狀态的函數(給函數添加字首“get”和“set”),特别是對取出和設定對象屬性的公共操作更是這樣。操作命名應當停留在類抽象和服務提供一級上;取出和設定對象屬性是低層次的實作細節,如果使它們公共化,則會降低封裝的完整性。

傳回布爾值(謂詞)的函數使用形容詞(或過去分詞)。謂詞經常使用名詞前添加字首 is 或 has 的形式使名稱表達一個肯定的聲明。當對象、類型名或枚舉型常量也使用簡單名稱時,這也同樣非常有用。時态上要保持一緻,力求準确。

示例
void insert(...);      
void erase(...);      
Name first_name();      
bool has_first_name();      
bool is_found();      
bool is_available();      

不要使用否定意義的名稱,因為這會導緻雙重否定表達式出現(如

!is_not_found

);這使得代碼更難以了解。有些情況下,通過使用反義詞,如“

is_invalid

”代替“

is_not_valid

”,可以使否定謂詞變成肯定的而不需改變其語義。

示例
bool is_not_valid(...);      
void find_client(name with_the_name, bool& not_found);      
Should be re-defined as:bool is_valid(...);      
void find_client(name with_the_name, bool& found);      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
當需要使用同一通用含義時,使用函數的重載

當操作目的相同時,使用重載而不使用同義詞;這盡量減少了系統中概念的數量和不同的操作,是以也就降低了整體的複雜性。

當重載操作符時,要確定操作符的語義保留了下來;如果不能保留約定俗成的操作符含義,則為函數選用另一個名稱而不使用操作符重載方式。

對象與函數參數

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
實參名使用文法元素強調其含義

為表明其唯一性或表明本實體是活動的主要核心,在對象或參數名稱前加字首“

the

”或“

this

”。要表明次要、臨時、輔助對象,則加字首“

a

”或“

current

”:

示例
void change_name( subscriber& the_subscriber,const subscriber::name new_name)      
{...the_subscriber.name = new_name;...}      
void update(subscriber_list& the_list,      
const subscriber::identification with_id,      
structure& on_structure,      
const value for_value);      
void change( object& the_object,const object using_object);      

異常

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
異常名選用否定的含義

隻有在處理錯誤時才必須使用異常,故使用名詞或名詞短語來清楚的表達一個否定的含義:

overflow, threshold_exceeded, bad_initial_value      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
異常名使用項目定義過的形容詞

使用項目定義清單中以下詞的一個 bad、incomplete、invalid、wrong、missing 或 illegal 作為名稱的一部分,而不要機械的套用 error 或 exception,因為它們并不表達具體的資訊。

其他事項

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
浮點指數和十六進制數使用大寫字母。

浮點數中的字母“E”和十六進制數從“A”到“F”應當一直保持大寫。

第五章

聲明

本章為不同 C++ 聲明類型的使用及形式提供指南。

名字空間

C++ 語言中名字空間存在之前,管理名稱的作用域隻有有限的幾種手段;是以,全局名字空間的使用過于泛濫,導緻衆多的沖突,這使同一程式中難以同時使用一些庫。新的名字空間語言特征解決了全局名字空間的幹擾問題。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
将全局聲明限定在名字空間中

這意味着隻有名字空間的名稱可以是全局的;所有其他的聲明都必須在某個名字空間的作用域内。

忽略了這一規則可能最終導緻名稱沖突。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用名字空間劃分非類功能

邏輯上劃分非類功能類的類别,或作用域遠大于類的功能(如庫或子系統),使用名字空間邏輯上統一的聲明(請參見“使用名字空間由系統或庫劃分潛在的全局名稱”)。

名稱表達了功能的邏輯劃分。

示例
namespace transport_layer_interface { /* ...*/ };      
namespace math_definitions { /* ...*/ };            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
盡量不使用全局和名字空間範圍的資料。

使用全局和名字空間範圍内資料與封裝原則是背道而馳的。

C++ 中類是基本的設計實施單元。使用它們紀錄域與設計的抽象,并作為實施抽象資料類型 (ADT) 的封裝機制。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用

class

而不是

struct

來實作抽象資料類型

使用

class

(類的關鍵字)而不是

struct

來實作一個表示抽象資料類型的類。

使用

struct

(類的關鍵字)來定義類似 C 中的舊式資料結構 (POD),尤其是在與 C 代碼程式接口時。

雖然

class

struct

是等價的,可以互換,但

class

具有更好的預設通路控制 (private),這樣就提供了更好的封裝性能。

基本原理

區分

class

struct

的的一緻做法引入了以上的語義差别,并且還超出了語言規則的範圍:

class

是紀錄抽象與封裝最先考慮的結構;而

struct

代表一個純資料結構,它可以在混合程式設計語言程式中交換使用。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
以可通路權限逐次降低的順序聲明類的成員

類聲明中的通路限定符應以順序 public, protected, private 出現。

基本原理

以 public、protected、private 順序排列的成員聲明保證了類使用者最感興趣的資訊置于最前列,是以減少了類使用者通路不相關資訊或實作細節。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
抽象資料類型避免聲明公共或保護資料成員

公共或保護資料成員的使用降低了類的封裝性并影響了系統抵抗變化的能力:公共資料成員将類的實作暴露給了它的使用者;保護資料成員将類的實作暴露給了從它繼承的類。類公共資料成員或保護資料成員的任何改變都會影響到使用者和繼承類。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用友元保留封裝性

初次碰到時,它好像是違反直覺的:友元關系将一個類的私有部分暴露給了它的友元,還如何實作封裝呢?當類之間高度互相依賴,需要互相間的内部資訊時,最好是賦予它們間友元關系而不是通過類的接口将其内部細節暴露出來。

不希望如同公共成員一樣暴露類的内部細節以提供給類客戶通路權。将保護成員暴露給潛在的繼承類,鼓勵了分級設計,但這也是不希望的。友元關系有選擇性的賦予了對私有成員的通路權,而不用實作子類限制。是以再所需通路之外保留了封裝。

将友元關系賦予友元測試類很好的說明了友元關系保留了封裝特性。友元測試類通過通路類的内部可完成相應的測試代碼,之後,友元測試類可從傳遞的代碼中删除。是以,沒有丢失封裝,也沒有向可傳遞代碼增加代碼。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免在類聲明中定義函數

類的聲明應當隻包含函數的聲明,永遠不要進行函數定義(實作)。

基本原理

類聲明中定義函數幹擾了類對其實作細節的規約;使類的接口難以辨認并難以閱讀;并增加了對編譯的依賴。

類聲明中的函數定義也減少了對内嵌函數的控制(請參照“使用

No_Inline

條件編譯符禁止内嵌編譯”)。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
明确聲明構造函數的類使用預設構造函數

為了在數組中或任何 STL 容器中使用類,類必須提供公共的預設構造函數或允許編譯器生成一個構造函數。

注意:

當類有非靜态引用類型的資料成員時是以上規則的一個例外,這種情況下通常不可能建立一個有意義的預設構造函數。是以,使用對象資料成員的引用就是不可靠的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
帶有指針類型資料成員的類要聲明其複制構造函數和指派操作符

如果需要,并且沒有明确聲明時,編譯器會為類暗中生成一個複制構造函數和一個指派操作符。編譯器定義了複制構造函數和指派操作符,在 Smalltalk 術語中這通常稱為“shallow-copy”(淺拷貝):明确的說,即按成員拷貝以及對指針按位拷貝。使用編譯器生成的複制構造函數和預設的指派操作符肯定會造成記憶體洩漏。

示例
//摘自 [Meyers, 92].void f(){String hello("Hello");      
//假設 String 類型      
//由指向 char 型      
//數組的指針來實作。      
{ //進入新的域(程式塊)      
String world("World");      
world = hello;        //指派語句使 world 丢失了      
//它最初指向的記憶體單元}      
//從域中退出時	//解構 world;      
//此時也間接的解構了 helloString hello2 = hello;      
//将已解構過的 hello 賦給                       // hello2}            

以上代碼中,儲存字元串“

World

”的記憶體單元在指派語句之後丢失了。結束内部程式塊時,銷毀了

world

;是以,由

hello

引用的記憶體單元也丢失了。已經解構的

hello

賦給了

hello2

示例
//摘自 [Meyers, 1992]。      
void foo(String bar) {};      
void f(){String lost = "String that will be lost!";      
foo(lost);}            

以上代碼中,當調用函數

foo

使用實參

lost

時,利用編譯器定義的複制構造函數将

lost

複制到

foo

中。因為複制

lost

時對指向“

String that will be lost!

”的指針進行逐位複制,當從

foo

中退出時,對

lost

的複制(即形參 bar,譯者注)将會被銷毀(假設析構函數正确的釋放了記憶體單元),同時存儲字元串“

String that will be lost!

”的記憶體單元也會被釋放。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要重複聲明構造函數參數有預設值

示例
//示例摘自 [X3J16, 95; section 12.8]      
class X {      
public:X(const X&, int);	           // int 參數沒有      
//初始化      
//沒有使用者聲明的複制構造函數,是以      
//編譯器暗中聲明了一個。      
};      
//int 參數滞後的初始化//将構造函數變成了複制構造函數。//      
X::X(const X& x, int i = 0) { ...}            
基本原理

編譯器在類聲明中沒有發現“标準”的複制構造函數時會暗中聲明一個複制構造函數。但是,預設參數滞後的初始化可能将構造函數改變成複制構造函數:使用複制構造函數時導緻混淆。因為這種混淆,任何複制構造函數的使用都是以而變為病态的。[X3J16, 95; section 12.8].

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
将析構函數總是聲明為 virtual 類型

除非明确将類的設計為不可繼承的,否則應将析構函數聲明為 virtual 類型。

基本原理

如果基類的析構函數沒有聲明為 virtual 類型,則通過基類的指針或引用删除派生類對象将導緻不确定的行為。

示例
//為了簡便起見,這裡使用了不好的命名風格      
class B {      
public:      
B(size_t size) {tp = new T[size]; }      
~B() { delete [] tp; tp = 0; }//...      
private:      
T* tp;};      
class D : public B {      
public:      
D(size_t size) : B(size) {}      
~D() {}//... };      
void f(){B* bp = new D(10);delete bp; //由于基類      
//的析構函數沒有定義成 virtual 類型,      
//這裡的行為是不确定的}            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免聲明太多的轉換操作符和單參數構造函數

使用限定符

explicit

可防止單參數構造函數用于隐式轉換。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要重定義非虛函數

非虛函數實作固定的行為,并且不希望專用于派生類。違反這一原則将導緻不可預料的行為:同一對象不同時間可能表現不同的行為。

非虛函數是靜态綁定的;以下示例中,對象函數的調用由變量(指向 A 或 B 的指針)的靜态類型控制,而不是對象的實際類型。

示例
// Adapted from [Meyers, 92].      
class A {      
public:oid f(); //非虛函數:靜态綁定      
};      
class B : public A {      
public:void f(); //非虛函數:靜态綁定      
};      
void g(){      
B x;      
A* pA = &x; //靜态類型,指向 A 的指針      
B* pB = &x; //靜态類型,指向 B 的指針      
pA->f(); //調用       
A::fpB->f(); //調用      
B::f      
}            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
謹慎使用非虛函數

因為非虛函數通過限制特殊化(即重載,譯者注)和多态的使用來限制子類,聲明為非虛拟之前,必須注意確定操作對所有子類确實是固定不變的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用初始化構造函數而不是使用構造函數中的指派語句

對象構造時其狀态的初始化應由初始化構造函數(一個成員初始化清單)完成,而不是通過構造函數内的指派操作符完成。

示例
代碼如下:      
class X {      
public:      
X();      
privateY       
the_y;      
};      
X::X() : the_y(some_y_expression) { }  “the_y”由初始化構造函數進行初始化而不是如下進行初始化      
X::X() { the_y = some_y_expression; } “the_y”由指派操作符進行初始化      
基本原理

對象的構造涉及到在執行構造函數體之前構造所有的基類以及資料成員。使用初始化構造函數隻需要一個操作(由初值構造),而在構造函數體中初始化資料成員需要兩個操作(構造以及指派)。

對有多重嵌套的類(類包含類,所包含的類又包含其他的類),多個操作(構造+成員指派)造成的性能的開支就十分重要了。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
初始化構造函數不要調用成員函數

示例
class A {      
public:      
A(int an_int);      
};      
class B : public A{      
public:      
int f();      
B();      
};      
B::B() : A(f()) {} //不确定:調用成員函數時A//尚未初始化[X3J16, 95].      
基本原理

當所有基類的成員初始化完成之前如果初始化構造函數直接或間接的調用了成員函數,此操作的結果是不确定的。[X3J16, 95].

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
注意構造函數和析構函數調用時的情況

構造函數中調用成員函數時要格外注意;即使調用的是虛函數時也要注意。執行的将是在類或其基類的構造函數、析構函數中定義的操作。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
整形類常量使用

static const

定義整形(整數)類常量時,使用

static const

資料成員,不要使用

#define

或全局常量。如果編譯器不支援

static const

,則使用

enum

示例
代碼如下:class X {      
static const buffer_size = 100;      
char buffer[buffer_size];      
};      
static const buffer_size;      
或:      
class C {      
enum { buffer_size = 100 };      
char buffer[buffer_size];      
};      
但不要使用:#define BUFFER_SIZE 100class C {char buffer[BUFFER_SIZE];};            

函數

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
一定要明确聲明函數的傳回值類型

這可以防止編譯器發現函數未聲明傳回值類型時所造成的模糊不清。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
函數聲明中要提供正規參數名稱

函數的聲明和定義中要使用相同的名稱;這可使混淆程度降至最小。提供參數名稱有利于提高代碼的易注釋性和易讀性。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
函數要盡量隻有一個傳回點

傳回語句在程式體中自由放置與

goto

語句的情況類似,會造成代碼難以閱讀和維護。

隻有在少數幾種函數中才能容忍出現多個傳回語句,此時可以同時看到所有的傳回語句

return

并且代碼還有非正常則的結構:

type_t foo(){if (this_condition)return this_value;elsereturn some_other_value;}            

函數的傳回類型為 void 時無傳回語句。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免建立對全局有副作用的函數

應盡量避免建立對全局有副作用的函數,因為它可能改變并不知情的資料而非它們内部對象的狀态,如全局和名字空間資料(另請參見“盡量不使用全局或名字空間範圍的資料”)但如果這是不可避免的,則應明确記錄下所有的副作用以作為函數規約的一部分。

隻在所需的對象中傳遞參數增加了代碼的上下文無關性,并提高了它的強壯性和易了解性。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
以重要性和活躍性遞減的順序聲明函數的參數

從調用者的角度講,參數聲明的順序是非常重要的:

  • 首先以重要性遞減的順序定義非預設參數;
  • 再以被修改可能性遞減的順序定義有預設值的參數。

這種順序使用了參數預設值的優點來減少函數調用中實參的個數。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免聲明帶有不定參數個數的函數

帶有不定參數個數的函數無法對其實參進行類型檢查。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免重複聲明帶有預設參數的函數

函數的進一步重複聲明中應避免添加預設值:遠離先前聲明,一個函數隻應聲明一次。否則,如果讀者沒有意識到此後的聲明,這将會造成混淆。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
函數聲明中盡可能使用 const

檢查函數是否有常量行為(傳回常數值;接受常量實參;或其操作不帶有副作用),如果有則使用限定符

const

表明其行為。

示例
const T f(...); //函數傳回一個常量對象。      
T f(T* const arg);	        //函數接受一個常量指針	      
//為其參數。      
//可以改變指針所指的對象,      
//但不可以改變指針本身。      
T f(const T* arg);      //函數參數為指針類型T       
f(const T& arg);      // 函數參數為引用類型      
//它們都指向一個常量對象。指針可以      
//改變,但所指的對象      
//不能改變。      
T f(const T* const arg);  //函數參數為       
//常量指針,它指向常量對象。      
//指針和它所指的對象      
//都不改變。      
T f(...) const;  //函數沒有副作用:      
//不改變它對象的狀态      
//是以可應用于      
//常量對象。      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免利用值傳遞對象

利用值傳遞和傳回對象可能帶來構造函數和析構函數的大量開支。可通過引用傳遞和傳回對象,這樣可避免構造函數和析構函數的開支。

可用

Const

引用指定通過引用傳遞的實參是不可改變的。複制構造函數和指派操作符是典型的示例:

C::C(const C& aC);      
C& C::operator=(const C& aC);      
示例:

考慮以下例子:

the_class the_class::return_by_value(the_class a_copy){      
return a_copy;      
}      
the_class an_object;      
return_by_value(an_object);      

調用函數

return_by_value

,其實參為

an_object

,此時調用

the_class

的複制構造函數将

an_object

複制到

a_copy

。再次調用

the_class

的複制構造函數将

a_copy

複制到函數傳回的臨時對象中。從函數傳回時調用

the_class

的析構函數銷毀

a_copy

。一段時間之後再次調用

the_class

的析構函數銷毀

return_by_value

傳回的對象。以上不完成任何事的函數的所有開支是兩個構造函數和兩個析構函數。

如果

the_class

是個派生類且包含其他類的成員資料,情況會更糟:會調用基類以及所包含類的構造函數和析構函數,是以函數調用所帶來的構造函數和析構函數的調用就會随之急劇增長。

注意:

以上指南似乎旨在建議開發人員在傳遞和傳回對象時總是使用引用類型,但應格外當心不要傳回引用到局部對象上,當需要對象時,也不要傳回引用。傳回引用到局部對象會造成災難性後果,因為函數傳回時,所傳回的引用綁定到了所銷毀的對象上。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不能傳回引用到局部對象

離開函數作用域時會銷毀局部對象;使用銷毀了的對象會造成災難。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不可傳回由 new 初始化,之後又已解除引用的指針

違背這一原則将導緻記憶體洩漏

示例
class C {      
public:      
...friend C& operator+( const C& left,const C& right);      
};      
C& operator+(const C& left, const C& right){      
C* new_c = new C(left..., right...);      
return *new_c;      
}      
C a, b, c, d;      
C sum;      
sum = a + b + c + d;      

因為在計算加和 sum 值時沒有存儲操作符“+”的中間結果,是以中間對象不能删除,否則會導緻記憶體洩漏。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要傳回非常量引用或指針到成員資料上

違背了這一原則也就違背了資料的封裝性,可能導緻不好的結果。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
宏擴充使用内嵌定義函數不使用

#define

但要有選擇的使用内嵌定義:隻對非常小的函數才使用;内嵌定義大型函數可能導緻代碼膨脹。

由于使用者代碼編譯時需要内嵌定義函數的實作,是以内嵌定義函數也增加了子產品間編譯的依賴。

[Meyers, 1992] 提供了以下不良宏使用極端示例的詳細讨論:

示例

不要這樣做:

#define MAX(a, b) ((a) > (b) ?(a) : (b))      

而應這樣做:

inline int max(int a, int b) { return a > b ? a : b; }      

MAX

有好幾個問題:它不是安全的類型;它的行為不是确定的:

int a = 1, b = 0;      
MAX(a++, b);     // a 增 1 了兩次      
MAX(a++, b+10);   // a 增 1 了一次      
MAX(a, "Hello"); //比較整形和指針      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用預設參數而不使用函數重載

當隻使用一個算法并且此算法可由少量幾個參數進行參數化時,使用預設參數而不使用函數重載。

使用預設參數有利于減少重載函數的數量、提高可維護性、減少函數調用時所需的實參數量、以及提高代碼的可讀性。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用函數重載表達常用語義

當同一語義操作需要多個實作時使用函數重載,但重載使用不同的實參類型。

重載操作符時保留其傳統含義。不要忘記定義關系操作符,如操作符

operator==

operator!=

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免重載以指針和整形數為實參的函數

避免使用帶單一整形實參的函數來重載帶單一指針實參的函數:

void f(char* p);      
void f(int i);      

以下調用可能會導緻混淆:

PRE>f(NULL); f(0);

此重載解析為

f(int)

而不是

f(char*)

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
令操作符

operator=

傳回對

*this

的引用

C++ 允許指派鍊:

String x, y, z;      
x = y = z = "A string";      

因為複制操作符是右結合的,将字元串“

A string

”賦給 z,再将 z 賦給 y,最後将 y 賦給 x。以從右到左的順序,對每個在 = 右端的表達式有效的調用一次

operator=

。這也意味着每一次

operator=

運算的結果都是一個對象,但其左端或右端的對象都有可能成為傳回值。

使用複制操作符好的做法總有以下形式:

C& C::operator=(const C&);      

隻有左端的對象是可能的(rhs 為常量引用,lhs 為變量引用),是以應傳回

*this

。詳情請參見[Meyers, 1992]。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使

operator=

檢查自指派

執行檢查有兩點重要的理由:首先,派生類對象的指派涉及到調用每一個基類(在繼承層次結構中位于此類的上方)的指派操作符,跳過這些操作符就可以節省很多運作時間。其次,在複制“rvalue”對象前,指派涉及到解構“lvalue”對象。在自指派時,rvalue 對象在指派前就已銷毀了,是以指派的結果是不确定的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
盡可能減少複雜性

不要書寫過長的函數,例如其代碼超過 60 行。

盡可能減少傳回語句的數量,1 是理想值。

盡量使循環複雜性降到 10 以下(對單一退出語句函數為判定語句總數加 1)。

盡量使擴充循環複雜性降到 15 以下(對單一退出語句函數為判定語句+邏輯操作符+1的和)。

盡量減小引用的平均最大作用範圍(局部對象聲明與對象第一次使用間的行距)。

類型

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
定義項目範圍的全局系統類型

大型項目中通常有整個系統中經常使用的類型的集合;這種情況下,将這些類型收集入一個或多個低級全局實用程式的名字空間中,這樣做是明智的(請參見“避免使用基本類型”的示例)。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免使用基本類型

當對可移植性要求較高、需要控制數字對象占據的記憶體空間或需要一個具體範圍的值時,不使用基本類型。這種情況下最好通過使用适當的基本類型明确聲明帶有大小限制的類型的名稱。

確定基本類型不會通過循環計數器、數組下标等潛回代碼中。

示例
namespace system_types {      
typedef unsigned char byte;      
typedef short int integer16; //16 位有符号整數      
typedef int integer32; //32 位有符号整數      
typedef unsigned short int natural16; //16 位無符号整數      
typedef unsigned int natural32; //32 位無符号整數...}            
基本原理

基本類型的表示依賴于具體實施。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用

typedef

建立同義詞來加強局部含義

對現有名稱使用

typedef

建立同義詞,提供更有意義的局部名稱并提高易讀性(這樣做不會增加運作時間)。

typedef

也可用來提供受限名稱的速記法。

示例
//标準庫的向量聲明//      
namespace std {template <class T, class Alloc = allocator>class vector {      
public:      
typedef typename Alloc::types<T>reference reference;      
typedef typename Alloc::types<T>const_reference const_reference;      
typedef typename Alloc::types<T>pointer iterator;      
typedef typename Alloc::types<T>const_pointer const_iterator;      
...}}            

使用

typedef

建立的名稱時,同一代碼段中不要混合使用原名稱和它的同義詞。

常量與對象

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

避免使用常量值

使用引用形式的已命名常量

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
定義常量時避免使用 #define 預處理訓示符

代之以

const

enum

不要這樣做:

#define LIGHT_SPEED 3E8而應這樣做:const int light_speed = 3E8;或調整數組大小如下:      
enum { small_buffer_size = 100, large_buffer_size = 1000 };      
基本原理

因為由

#defines

引入的名稱在編譯預處理時會被替換掉,不出現在符号表中,是以調試就更加困難了。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
對象聲明靠近其第一次使用點
C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
聲明時總要初始化 const 對象

未聲明為

extern

const

對象有内部連結,聲明時初始化這些常量對象允許在編譯時使用初始化函數。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
永遠不要忘記常量對象的“不變性”

常量對象可能存在于隻讀存儲器中。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
定義時初始化對象

如果對象不是自初始化的,在對象定義中指定初始值。如果不可能指定一個有意義的初始值,則指派“nil”或考慮後面再作定義。

對大型對象,通常不建議先構造對象,此後再使用指派語句初始化的作法。因為這樣做的代價高昂(請參見“使用初始化構造函數而不使用構造函數中的指派語句”)。

如果構造時不可能合适的初始化對象,則使用傳統的“nil”值進行對象初始化,它意味着“未初始化”。隻在初始化過程中聲明“不可用但值已知”的對象時才使用 nil 值。但算法在受控模式下可以拒絕接受:在對象合适的初始化之前使用它時可以指出這是未經初始化的變量錯誤。

注意有時不可能所有類型都聲明為 nil 值,尤其是在模運算類型中,例如角度。I這種情況下選擇最不可能出現的值。

第六章

表達式和語句

本章對不同種類的 C++ 表達式和語句提供指南。

表達式

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用備援的圓括号使複合表達式含義更加清晰
C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免表達式的過深嵌套

表達式的嵌套層數定義為:忽略操作符優先級原則時,從左到右求表達式的值所需圓括号對的嵌套數。

嵌套層數過多會使表達式難以了解。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要假定任何特殊的表達式計算次序

除非計算次序由操作符指定(如逗号操作符、三元表達式、以及連接配接和分離),否則不要假定任何特殊的計算次序,因為這種假定可能導緻嚴重的混淆并降低可移植性。

例如,同一語句中不要混合使用變量的增 1 和減 1 操作。

示例
foo(i, i++);array[i] = i--;      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
空指針使用 0 而不使用

NULL

空指針應使用 0 還是

NULL

是一個有高度争議的論題。

C 和 C++ 中定義任何零值常量表達式都可解釋為空指針。因為零難以閱讀,并且不贊成使用常量,傳統上程式員使用宏

NULL

作為空指針。不幸的是,

NULL

沒有可移植的定義。一些 ANSI C 編譯器使用 (void *)0,但對 C++ 來說這不是一個好的選擇:

char* cp = (void*)0; /* C 合法但 C++ 不合法*/      

是以任何形如 (T*)0 而非一個 0 的

NULL

定義,在C++ 中都需要類型轉換。曆史上,很多指南都擁護使用 0 代替空指針以減少類型轉換、增強代碼的可移植性。但許多 C++ 開發人員還是感覺使用

NULL

比 0 更加舒服,也争辯說當今多數的編譯器(準确的說是多數頭檔案)将

NULL

實施為 0。

本指南傾向使用 0,因為不管

NULL

的值是多少,0 都是有效的。但由于争議,這一點降級為一個小技巧,可在适當的時候遵循或忽略它。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要使用舊類型轉換

使用新類型轉換符(

dynamic_cast、static_cast、reinterpret_cast、const_cast

)而不要使用舊類型轉換符。

如果您沒有新類型轉換符,避免同時轉換,特别是向下轉換(将基類對象轉換成派生類對象)。

使用轉換操作符如下:

  • dynamic_cast

    - 類同一層次(子類型)成員間的的轉換,使用運作類型資訊(對帶虛函數的類,運作類型資訊是可用的)。這種類間的轉換一定是安全的。
  • static_cast

    - 類同一層次成員間的的轉換,不使用運作類型資訊;是以這可能是不安全的。如果程式員不能保證類型的安全性,則使用

    dynamic_cast。

  • reinterpret_cast

    - 不相關指針類型和整形(整數)間的轉換;這是不安全的,隻應在所提及的類型間使用。
  • const_cast

    - 将指定為

    const

    型函數實參的“不變性”轉換掉。注意:

    const_cast

    不打算将确實定義為常量對象(它可能位于隻讀存儲器中)的“不變性”轉換掉。

不要使用

typeid

實作類型轉換邏輯:使類型轉換操作符完成類型檢查和轉換的操作不可再分,詳情請參見 [Stroustrup, 1994]。

示例

不要這樣做:

void foo (const base& b){      
if (typeid(b) == typeid(derived1))      
{      
do_derived1_stuff();      
else if (typeid(b) == typeid(derived2))       
{do_derived2_stuff();      
else if () {}}            
基本原理

舊類型轉換使類型系統失效,可能導緻編譯器難以察覺的缺陷:記憶體管理系統可能會崩潰,虛函數表可能遭嚴重錯誤修改,當對象作為派生類對象通路時無關對象可能會遭破壞。注意即使讀通路也可能造成破壞,因為有可能引用了并不存在的指針或區域。

新類型轉換操作符使類型轉換更加安全(多數情況下)、更加明确。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
Boolean(布爾)表達式使用新的 bool 類型

不要使用舊的 Boolean 宏或常數:沒有标準的布爾值 true(真);代之以新的

bool

類型。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
布爾值 true(真)之間不要直接比較

因為傳統上 true 值 (1 或 !0) 沒有标準值,非零表達式和真值的比較就可能無效。

代之以使用布爾表達式。

示例

不要這樣做:

if (someNonZeroExpression == true) //可能不會解釋為真值條件最好這樣做:      
if (someNonZeroExpression) //一定會解釋為一個真值條件      

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
指針不要與不在同一數組中的對象比較

這種操作的結果幾乎都是毫無意義的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
已删除的對象指針要賦予空指針值

設定已删除對象的指針為空指針可避免災難的發生:重複删除非空指針是有害的,但重複删除空指針是無害的。

即使在函數傳回前,删除操作後也總要賦一個空指針,因為以後可能添加新的代碼。

語句

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
當從布爾表達式分支時使用 if 語句
C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
當從離散值分支時使用 switch 語句

當分支條件為離散值時使用 switch 語句而不使用一系列的“else if”。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
一定為 switch 語句提供一個

default

分支以記錄錯誤

switch 語句應總包含一個

default

分支,

default

分支應用以捕獲錯誤。

這一原則保證了當引入新的 switch 值而處理這一新值的分支被删除時,現有的 default 分支可以捕獲到錯誤。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
當循環需要疊代前測試時使用 for 語句或 while 語句

當疊代和循環的結束是根據循環計數器的值時使用 for 語句不使用 while 語句。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
當循環需要疊代後測試時使用 do while 語句
C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
循環中避免使用 jump 語句

避免使用除循環結束條件以外的循環退出方式(使用

break、return

goto

),不要使用

continue

不成熟的跳轉到下一疊代上。這減少了控制路徑流程的數量,使代碼更易了解。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要使用

goto

語句

這是一條通用原則。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免嵌套作用域内隐藏辨別符

這會使讀者模糊不清,維護時造成潛在的危險。

第七章

特殊主題

本章為記憶體管理與錯誤報告提供指南

記憶體管理

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
C 和 C++ 記憶體操作要避免混合使用

C 庫函數

malloc

calloc

realloc

不應用于配置設定對象空間:此時應使用 C++ 操作符 new。

隻有在記憶體傳遞給C 庫函數處理時,才使用 C 函數配置設定記憶體。

不要使用 delete 來釋放由 C 函數配置設定的記憶體,或釋放由 new 建立的對象。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
删除由 new 建立的數組對象時總使用 delete[]

使用 delete 删除數組對象時如果不使用空方括号(“[]”),則隻删除數組的第一個元素,是以導緻記憶體洩漏。

錯誤處理和異常

由于使用 C++ 異常機制的經驗不足,此處的指南将來可能會作較大的改變。

C++ 草拟标準定義了兩大類錯誤:邏輯錯誤和運作錯誤。邏輯錯誤是可避免的程式設計錯誤。運作錯誤定義為程式範圍外事件導緻的錯誤。

使用異常的通用規則為:正常條件下的系統如果沒有重載或硬體錯誤則不應當産生任何異常。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
開發過程中多使用聲明語句以發現錯誤

開發過程中使用函數的前置和後置條件聲明語句以發現“倒斃”錯誤。

在實作最終錯誤處理代碼前,聲明語句提供了簡單有用的臨時錯誤監測機制。聲明語句還有額外的好處,使用“NDEBUG”預處理符,可在編譯時去掉這些聲明(請參見“用具體值定義 NDEBUG 預處理符”)。

傳統上使用宏 assert 達到這一目的;但文獻 [Stroustrup, 1994] 提供了一個可替代的模闆如下。

示例
template<class T, class Exception>inline void assert ( T a_boolean_expression, Exception the_exception){if (! NDEBUG)if (! a_boolean_expression) throw the_exception;}            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
隻在真實的異常情況時使用異常

對經常的、預料中的事件不要使用異常:異常打斷了代碼的正常控制流程,使它難以了解和維護。

預料中的事件應用代碼的正常控制流程處理;需要時使用函數傳回值或“out”參數狀态碼。

異常也不應用于實作控制結構:這會是另一種形式的“goto”語句。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
從标準異常中派生項目異常

這保證了所有異常都支援常用操作的最小集合并可由一小組進階處理程式處理。

使用邏輯錯誤(域錯誤、非法實參錯誤、長度錯誤和越界錯誤)表示應用程式域錯誤、函數調用傳遞了非法實參、對象構造超出了它們允許的大小以及實參值不在允許範圍内。

使用運作錯誤(範圍錯誤和溢出錯誤)表示隻有在運作時才能查出的算術和配置錯誤、資料已遭破壞或資源耗盡錯誤。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
給定抽象盡量少使用異常

大型系統中,每一層都不得不處理大量的異常會使代碼難以閱讀和維護。異常處理可能會嚴重影響了正常處理。

減少使用異常的方法有:

通過使用少量的異常種類在抽象間共享異常。

引發派生于标準異常的特殊異常,處理更通用的異常。

為對象添加“異常”狀态,提供明确檢查對象合法性的原語。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
将所有異常聲明為已引發

産生異常(而不僅僅是傳遞異常)的函數應在它們的異正常約中将所有異常聲明為已引發:它們不應平靜的産生異常而不警告它們的使用者。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
在異常首次出現時就報告它

開發過程中,采用合适的記錄機制盡早報告異常,包括在“異常引發點”。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
按照從派生結構最底端到最頂端的順序定義異常處理

定義異常處理要按照從派生結構最底端到最頂端(即從最終子類到最初父類)的順序,這樣可以避免編寫無法到達的處理過程;請參見下面的示例 how-not-to-it。因為是以聲明的順序比對處理過程的,是以這也保證了異常由最合适的處理過程捕獲。

示例

不要這樣做:

class base { ...};      
class derived : public base { ...};      
...try {...throw derived(...);引發一個派生類異常}      
catch (base& a_base_failure)但基類異常處理過程“捕獲”了它,因為//基類處理過程首先比對!      
{...}      
catch (derived& a_derived_failure) 這一處理過程是無法達到的!{ ...}            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
避免使用捕獲所有異常的處理過程

避免使用捕獲所有異常的處理過程(處理過程聲明使用 “…”),除非重引發了異常隻有在做本地内務管理時才使用捕獲所有異常的處理過程,這時應重引發異常以避免掩蓋了本層不能處理此異常的事實。

try {...}catch (...){ if (io.is_open(local_file)){io.close(local_file);}throw;}            

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
確定函數狀态代碼有合适的值

當狀态代碼作為函數參數傳回時,要指派給參數作為程式體中的第一個可執行語句。系統的設定所有狀态的預設值為成功或失敗。考慮函數所有可能的出口,包括異常處理。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
本地執行安全檢查;不要希望您的客戶會這樣做

如果沒有合适的輸入,函數就會産生錯誤輸出,在函數中裝載代碼以受控方式監測和報告非法輸入。不要依賴于标注來告知客戶傳遞合适的值。事實上标注遲早會被忽略掉,如果檢測到非法參數就會導緻難以調試的錯誤。

第八章

可移植性

本章論述先驗不可移植的語言特征。

路徑名

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要使用絕對代碼檔案路徑名

各作業系統中的路徑名不是以标準形式表示的。使用它們将會引入對平台的依賴。

示例
#include "somePath/filename.hh" // Unix      
#include "somePath/filename.hh" // MSDOS      

資料表示

類型的表示和排列高度依賴于機器的結構。對表示和排列的假設會導緻混淆,降低可移植性。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要假設類型的表示

特别的,不可以在

int

、長整形或任何其他數字類型中存儲指針類型,因為這是高度不可移植的。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要假設類型的排列

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

不要依賴于一個特殊的下溢或上溢行為

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻

盡可能使用“可伸縮”常量

可伸縮常量避免了字長變化的問題。

示例
const int all_ones = ~0;const int last_3_bits = ~0x7;      

類型轉換

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
不要從一個“短”類型轉換成一個“長類型”

機器結構可能指定了某種類型的排列方式。從需要較松散排列的類型轉換為需要較嚴密排列的類型可能會導緻程式錯誤。

第九章

複用

本章為 C++ 代碼複用提供指南。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
盡可能使用标準庫構件

如果沒有标準庫,則基于标準庫接口建立類,這有利于将來的移植。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用模闆複用獨立于資料的行為

當行為不依賴于一個具體的資料類型時,使用模闆複用行為。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用公共繼承複用類接口(子類型化)

使用

public

繼承表達“is a”關系并複用基類接口,還可有選擇的複用它們的實作。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
使用包容而不是私有繼承複用類的實作

當複用實作或模組化“部分/整體”關系時,避免使用私有繼承。複用未重新定義的實作最好由包容而非私有繼承來實作。

當需要重新定義基類的操作時使用私有繼承。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
謹慎使用多繼承

多繼承應謹慎使用,因為它帶來了許多額外的複雜性。[Meyers, 1992] 提供了對潛在名稱歧義和重複繼承所帶來複雜性的詳細讨論。複雜性來自于:

歧義,當多個類使用相同的名稱時,任何對名稱不加限定的引用天生就是産生歧義的。可通過使用類名稱限定其成員名稱來解決歧義的問題。但這也帶來了不幸的後果:使多态無效且将虛函數準變成了靜态綁定函數。

從同一基類重複繼承(派生類通過繼承結構的不同路徑多次繼承一個基類)多組資料成員産生了如下問題:應當使用多組資料成員中的哪一個?

可使用虛繼承(對虛基類的繼承)防止多繼承資料成員。那麼為什麼不總使用虛繼承呢?虛繼承有負面影響,會改變基礎對象表示并降低通路的效率。

要求所有的繼承都是虛拟的,同時也就強加了包羅一切的空間,帶來了時間上的低效,實作這樣的政策過于獨斷了。

是以,為了決定是使用虛繼承還是非虛繼承,多繼承需要類的設計者對将來類的使用有敏銳的洞察力。

第十章

編譯問題

本章為編譯問題指南

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
盡量減少對編譯的依賴

子產品規約中不要包含隻是此子產品實施所需的其他頭檔案。

避免在隻需要指針或引用可見時就為了能看到其他類而在規約中包含頭檔案;代之以預先聲明。

示例
//子產品 A 規約,包含于檔案“A.hh”中#include "B.hh" //當隻有實施需要時                //不要包含。#include "C.hh" //隻有引用需要時不要包含;                //代之以預先聲明。class C;class A{C* a_c_by_reference; //有 a 的引用。};// “A.hh”結束      
注意:

盡量減少對編譯的依賴是某些設計代碼模式或模式的基本原理,如不同的命名:Handle(句柄)或 Envelope(信包)[Meyers, 1992],或 Bridge(橋)[Gamma] 類。将類抽象的責任在兩個關聯類間分割,一個提供類接口,另一個提供類實作;因為任何實作(實作類)上的改變都不再需要客戶重新編譯,是以類與其客戶間的依賴關系就最小化了。

示例
//子產品 A 規約,包含于檔案“A.hh”中      
class A_implementation;class A{A_implementation* the_implementation;};      
//“A.hh”結束      

這一做法也允許接口類和實作類特殊化為兩個單獨的類層次結構。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
用具體值定義

NDEBUG

通常使用符号

NDEBUG

在編譯過程中去掉由宏 assert 實作的聲明代碼。傳統做法是當需要消除聲明語句時定義這一符号;但是,開發人員經常沒有意識到聲明的存在,是以沒有定義符号。

我們支援使用聲明的模闆版本;這種情況下符号

NDEBUG

必須明确的指派:需要聲明代碼時為 0;要消除時為非 0。任何沒有提供符号

NDEBUG

以具體值的聲明代碼最終編譯時将産生編譯錯誤;是以,提醒了開發人員注意聲明代碼的存在。

指南總結

以下是本手冊所有指南的總結。

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
要求或限制

使用常識

通常使用

#include

來通路子產品的規約

永遠不要聲明以一個或多個下劃線 ('_') 開頭的名稱

将全局聲明限定在名字空間中

明确聲明構造函數的類使用預設構造函數

帶有指針類型資料成員的類要聲明其複制構造函數和指派操作符

不要重複聲明構造函數參數有預設值

将析構函數總是聲明為 virtual 類型

不要重定義非虛函數

初始化構造函數不要調用成員函數

不能傳回引用到局部對象

不可傳回由 new 初始化,之後又已解除引用的指針

不要傳回非常量引用或指針到成員資料上

令操作符

operator=

傳回對

*this

的引用

使

operator=

檢查自指派

永遠不要忘記常量對象的“不變性”

不要假定任何特殊的表達式計算次序

不要使用舊類型轉換

Boolean(布爾)表達式使用新的 bool 類型

布爾值 true(真)之間不要直接比較

指針不要與不在同一數組中的對象比較

已删除的對象指針要賦予空指針值

一定為 switch 語句提供一個 default 分支以記錄錯誤

不要使用 goto 語句

C 和 C++ 記憶體操作要避免混合使用

删除由 new 建立的數組對象時總使用 delete[]

不要使用絕對代碼檔案路徑名

不要假設類型的表示

不要假設類型的排列

不要依賴于一個特殊的下溢或上溢行為

不要從一個“短”類型轉換成一個“長類型”

用具體值定義

NDEBUG

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
建議

不同檔案中放置子產品規約與實作

隻選擇一組檔案擴充名以區分頭檔案和實施檔案

每個子產品規約避免定義多個類

避免将私有實施聲明置于子產品的規約中

将子產品内嵌函數的定義置于單獨的檔案中

如果程式規模是個要求考慮的問題,就把大的子產品分割成多個變換單元

分離平台依賴性

防止重複檔案包含的保護

使用"

No_Inline

"條件編譯符破壞内嵌編譯

對嵌套語句使用小的、一緻的縮進風格

相對于函數名或作用域名縮進函數參數

使用能夠适合标準列印紙大小的最大行寬

使用一緻換行

使用 C++ 風格的注釋而非 C 風格的注釋

注釋與源代碼盡可能靠攏

避免行末注釋

避免注釋頭

使用空注釋行分離注釋段

避免備援

編寫自記錄代碼而非注釋

記錄類與函數

選擇一個名稱約定,使用它要前後一緻

避免使用隻靠字母大小寫才能區分的名稱

避免使用縮寫

避免使用字尾來表明程式語言結構

選擇清晰的、易辨認的、有意義的名稱

使用名稱的正确拼寫

布爾值使用正值謂詞從句

使用名字空間由子系統或庫劃分潛在全局名稱

類的名稱使用名詞或名詞短語

過程類型的函數名稱使用動詞

當需要使用同一通用含義時,使用函數的重載

實參名使用文法元素強調其含義

異常名選用否定的含義

異常名使用項目定義過的形容詞

浮點指數和十六進制數使用大寫字母

使用名字空間劃分非類功能

盡量不使用全局和名字空間範圍的資料

使用

class

而不是

struct

來實作抽象資料類型

以可通路權限逐次降低的順序聲明類的成員

抽象資料類型避免聲明公共或保護資料成員

使用友元保留封裝性

避免在類聲明中定義函數

避免聲明太多的轉換操作符和單參數構造函數

謹慎使用非虛函數

使用初始化構造函數而不是使用構造函數中的指派語句

注意構造函數和析構函數調用時的情況

整形類常量使用

static const

一定要明确聲明函數的傳回值類型

函數聲明中要提供正規參數名稱

函數要盡量隻有一個傳回點

避免建立對全局有副作用的函數

以重要性和活躍性遞減的順序聲明函數的參數

避免聲明帶有不定參數個數的函數

避免重複聲明帶有預設參數的函數

函數聲明中盡可能使用 const

避免利用值傳遞對象

宏擴充使用内嵌定義函數不使用

#define

使用預設參數而不使用函數重載

使用函數重載表達常用語義

避免重載以指針和整形數為實參的函數

盡可能減少複雜性

避免使用基本類型

避免使用常量值

定義常量時避免使用

#define

預處理訓示符

對象聲明靠近其第一次使用點

聲明時總要初始化 const 對象

定義時初始化對象

當從布爾表達式分支時使用 if 語句

當從離散值分支時使用 switch 語句

當循環需要疊代前測試時使用 for 語句或 while 語句

當循環需要疊代後測試時使用 do while 語句

循環中避免使用 jump 語句

避免嵌套作用域内隐藏辨別符

開發過程中多使用聲明語句以發現錯誤

隻在真實的異常情況時使用異常

從标準異常中派生項目異常

給定抽象盡量少使用異常

将所有異常聲明為已引發

按照從派生結構最底端到最頂端的順序定義異常處理

避免使用捕獲所有異常的處理過程

確定函數狀态代碼有合适的值

本地執行安全檢查;不要希望您的客戶會這樣做

盡可能使用“可伸縮”常量

盡可能使用标準庫構件

C++大型項目開發限制簡介代碼組織與風格注釋命名聲明表達式和語句特殊主題可移植性複用編譯問題指南總結參考文獻
提示

定義項目範圍的全局系統類型

使用

typedef

建立同義詞來加強局部含義

使用備援的圓括号使複合表達式含義更加清晰

避免表達式的過深嵌套

空指針使用 0 而不使用

NULL

在異常首次出現時就報告它

參考文獻

[Cargill, 92]

Cargill, Tom.1992. C++ Programming Styles Addison-Wesley.

[Coplien, 92] Coplien, James O. 1992. Advanced C++ Programming Styles and Idioms, Addison-Wesley.
[Ellemtel, 93] Ellemtel Telecommunications Systems Laboratories.June 1993. Programming in C++ Rules and Recommendations.
[Ellis, 90] Ellis, Margaret A. and Stroustrup, Bjarne.1990.The Annotated C++ Reference Manual, Addison-Wesley.
[Kruchten, 94] Kruchten, P. May 1994. Ada Programming Guidelines for the Canadian Automated Air Traffic System.
[Lippman, 96] Lippman, Stanley, B. 1996. Inside the C++ Object Model, Addison-Wesley.
[Meyers, 92] Meyers, Scott.1992. Effective C++, Addison-Wesley.
[Meyers, 96] Meyers, Scott.1996. More Effective C++, Addison-Wesley.
[Plauger, 95] Plauger, P.J. 1995. The Draft Standard C++ Library, Prentice Hall, Inc.
[Plum, 91] Plum, Thomas and Saks, Dan.1991. C++ Programming Guidelines, Plum Hall Inc.
[Rational, 92] Rational Software Corporation, December 1992. Rose C++ Programming Style Guidelines.
[Stroustrup, 94] Stroustrup, Bjarne.1994. The Design and Evolution of C++, Addison-Wesley.
[X3J16, 95] X3J16/95-0087 | WG21/N0687.April 1995. Working Paper for Draft Proposed International Standard for Information Systems-Programming Language C++.
[Cargill, 92] Cargill, Tom.1992. C++ Programming Styles Addison-Wesley.
[Coplien, 92] Coplien, James O. 1992. Advanced C++ Programming Styles and Idioms, Addison-Wesley.
[Ellemtel, 93] Ellemtel Telecommunications Systems Laboratories.June 1993. Programming in C++ Rules and Recommendations.
[Ellis, 90] Ellis, Margaret A. and Stroustrup, Bjarne.1990.The Annotated C++ Reference Manual, Addison-Wesley.
[Kruchten, 94] Kruchten, P. May 1994. Ada Programming Guidelines for the Canadian Automated Air Traffic System.
[Lippman, 96] Lippman, Stanley, B. 1996. Inside the C++ Object Model, Addison-Wesley.
[Meyers, 92] Meyers, Scott.1992. Effective C++, Addison-Wesley.
[Meyers, 96] Meyers, Scott.1996. More Effective C++, Addison-Wesley.
[Plauger, 95] Plauger, P.J. 1995. The Draft Standard C++ Library, Prentice Hall, Inc.
[Plum, 91] Plum, Thomas and Saks, Dan.1991. C++ Programming Guidelines, Plum Hall Inc.
[Rational, 92] Rational Software Corporation, December 1992. Rose C++ Programming Style Guidelines.
[Stroustrup, 94] Stroustrup, Bjarne.1994. The Design and Evolution of C++, Addison-Wesley.
[X3J16, 95] X3J16/95-0087 | WG21/N0687.April 1995. Working Paper for Draft Proposed International Standard for Information Systems-Programming Language C++.