• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            牽著老婆滿街逛

            嚴(yán)以律己,寬以待人. 三思而后行.
            GMail/GTalk: yanglinbo#google.com;
            MSN/Email: tx7do#yahoo.com.cn;
            QQ: 3 0 3 3 9 6 9 2 0 .

            C++ 編程指南

            版權(quán)所有 ? 1997 Rational Software Corporation。
            保留所有權(quán)利。

            “Rational”一詞和 Rational 產(chǎn)品名是 Rational Software Corporation 的商標(biāo)。涉及到其他公司及其產(chǎn)品時(shí)將使用各自公司的商標(biāo)且僅限于此目的。

            此文檔由來自 Calypso Software Inc. (Vancouver, B.C., Canada) 的 Luan Doan-Minh 為 Rational Software Corp. 編寫。


            目錄

            簡(jiǎn)介

            基本原則
            前提
            指南分類
            最根本的原則

            代碼組織與風(fēng)格

            代碼結(jié)構(gòu)
            代碼風(fēng)格

            注釋

            命名

            概述
            名字空間

            函數(shù)
            對(duì)象與函數(shù)參數(shù)
            異常
            其他事項(xiàng)

            聲明

            名字空間

            函數(shù)
            類型
            常量與對(duì)象

            表達(dá)式和語句

            表達(dá)式
            語句

            特殊主題

            內(nèi)存管理
            錯(cuò)誤處理和異常

            可移植性

            路徑名
            數(shù)據(jù)表示
            類型轉(zhuǎn)換

            復(fù)用

            編譯問題

            指南總結(jié)

            要求或限制
            建議
            提示

            參考文獻(xiàn)


            第一章

            簡(jiǎn)介

            大型軟件項(xiàng)目通常由相應(yīng)的大型開發(fā)團(tuán)隊(duì)承擔(dān)。大型團(tuán)隊(duì)生成的代碼要有項(xiàng)目范圍內(nèi)可評(píng)測(cè)的質(zhì)量,代碼必須遵從于某一標(biāo)準(zhǔn)并以此來評(píng)價(jià)。因此,對(duì)大型的項(xiàng)目團(tuán)隊(duì)來說,建立一個(gè)編程的標(biāo)準(zhǔn)或一組指南很重要。
            使用編程標(biāo)準(zhǔn)也使以下各項(xiàng)成為可能:

            • 增加開發(fā)過程代碼的強(qiáng)壯性、可讀性、易維護(hù)性;減少有經(jīng)驗(yàn)和無經(jīng)驗(yàn)開發(fā)人員編程所需的腦力工作;
            • 在項(xiàng)目范圍內(nèi)統(tǒng)一代碼風(fēng)格;
            • 通過人為以及自動(dòng)的方式對(duì)最終軟件應(yīng)用質(zhì)量標(biāo)準(zhǔn);
            • 使新的開發(fā)人員快速適應(yīng)項(xiàng)目氛圍;
            • 支持項(xiàng)目資源的復(fù)用:允許開發(fā)人員從一個(gè)項(xiàng)目區(qū)域(或子項(xiàng)目團(tuán)隊(duì))移動(dòng)到另一個(gè),而不需要重新適應(yīng)新的子項(xiàng)目團(tuán)隊(duì)的氛圍。

            本文的目的是表述 C++ 編程的規(guī)則、指南和提示(通常也稱之為指南),它們可用來作為編程標(biāo)準(zhǔn)的基礎(chǔ)。對(duì)工作在大型項(xiàng)目團(tuán)隊(duì)的軟件工程師,這些都是需要的。
            當(dāng)前版本專注于程序的編制(雖然現(xiàn)在還很難在設(shè)計(jì)和編程間劃定明確的界限);以后將會(huì)添加設(shè)計(jì)指南。
            指南包括了以下 C++ 開發(fā)的方面:

            • 如何組織項(xiàng)目代碼;
            • 編程風(fēng)格(如何編寫實(shí)際的源代碼);
            • 如何記錄源代碼;
            • 代碼內(nèi)名稱和源文件所使用的命名約定;
            • 何時(shí)使用某些語言結(jié)構(gòu)以及何時(shí)應(yīng)避免某些語言結(jié)構(gòu)。

            它們從大量的行業(yè)知識(shí)中搜集而來。(請(qǐng)參見參考文獻(xiàn):作者及參考文獻(xiàn)。)它們基于:

            • 廣為人知的軟件原理;
            • “好的”軟件實(shí)現(xiàn);
            • 所學(xué)課程;
            • 主觀意愿。

            大多會(huì)基于幾個(gè)第一種類型,也會(huì)基于大量的第二種和第三種類型。不幸的是,某些也基于最后一種類型;這主要是因?yàn)榫幊淌且粋€(gè)高度主觀的活動(dòng):編程沒有普遍接受的最好或正確的萬靈藥。

            基本原則

            清晰、可理解的 C++ 源代碼是規(guī)則和指南的主要目標(biāo):清晰、可理解的源代碼是軟件可靠性和可維護(hù)性的主要作用因素。清晰、可理解的代碼可以表示為以下三個(gè)簡(jiǎn)單的基礎(chǔ)原理 [Kruchten, 94]
            最小混淆 - 它的生存期中,源代碼的讀遠(yuǎn)比寫多,規(guī)約更是這樣。理想情況下,源代碼讀起來應(yīng)該象英語一樣描述了所要做的事,這同時(shí)還帶來了它執(zhí)行的好處。程序更多是為人編寫,而不是為計(jì)算機(jī)而編寫。閱讀代碼是一個(gè)復(fù)雜的腦力過程,它可由統(tǒng)一標(biāo)準(zhǔn)來簡(jiǎn)化,在本文中還指最小混淆原則。整個(gè)項(xiàng)目中統(tǒng)一樣式是軟件開發(fā)團(tuán)隊(duì)在編程標(biāo)準(zhǔn)上達(dá)成一致的主要原因,它不應(yīng)視為一種懲罰或?qū)?chuàng)造性和生產(chǎn)力的阻礙。
            維護(hù)的唯一點(diǎn) - 只要可能,設(shè)計(jì)決策就應(yīng)在源中只表述一點(diǎn),它的多數(shù)后果應(yīng)程序化的派生于此點(diǎn)。不遵守這一原則嚴(yán)重?fù)p害了可維護(hù)性、可靠性和可理解性。
            最小干擾 - 最終,應(yīng)用最小干擾原則(它是易讀性的主要作用因素)。即,避免將源代碼與可視干擾(如內(nèi)容較少或?qū)斫廛浖康牟黄鹱饔玫男畔ⅲ┫嗷旌希?br />指南這里所要表達(dá)的精神是不要過于苛刻;而對(duì)正確安全的使用語言特性提供指導(dǎo)。優(yōu)秀軟件的關(guān)鍵在于:
            了解每一個(gè)特性以及它的限制和潛在的危險(xiǎn);
            確切了解此特性可安全的使用于哪一個(gè)環(huán)境中;
            做出使用高度可視特性的決定;
            在合適的地方小心適度的使用特性。

            前提

            這里的指南只有少數(shù)幾個(gè)基本前提:
            讀者了解 C++。
            任何有益的地方都鼓勵(lì)使用 C++ 的高級(jí)特性,而不是只允許使用一些程序員都不熟悉的低級(jí)特性。這是項(xiàng)目能從使用 C++ 中獲益的唯一方式。C++ 不應(yīng)只當(dāng)成 C 來使用,事實(shí)上 C++ 的面向?qū)ο筇匦允顾粫?huì)象 C 一樣使用。不鼓勵(lì)將代碼直譯成注釋;相反,在任何可能的地方應(yīng)當(dāng)使用源代碼代替注釋。
            遵從大型項(xiàng)目的做法。
            即使只是為了項(xiàng)目一級(jí)或公司一級(jí)的實(shí)現(xiàn)和統(tǒng)一,大型項(xiàng)目中許多規(guī)則仍很有價(jià)值,它們?cè)谛⌒拖到y(tǒng)中也有使用。
            代碼遵從于一個(gè)面向?qū)ο笤O(shè)計(jì)。
            許多規(guī)則都會(huì)支持從面向?qū)ο?(OO) 概念到 C++ 特性和具體命名約定的系統(tǒng)映射。

            指南分類

            指南具有不同的重要性;其重要性由以下標(biāo)準(zhǔn)來衡量:


            提示:
            以上符號(hào)所確定的指南只是一個(gè)小提示或小建議,即使忽略它也沒關(guān)系。
            建議:
            以上符號(hào)所確定的指南是一個(gè)建議,它通常是建立在更加技術(shù)性的基礎(chǔ)之上:封裝、內(nèi)聚、耦合、可移植性或可復(fù)用性與某些實(shí)現(xiàn)中的性能一樣都會(huì)受到影響。除非有合適的理由,否則必須遵從建議。

            要求或限制:
            以上符號(hào)確定的一個(gè)指南就是一個(gè)要求或限制;不遵從它肯定會(huì)導(dǎo)致壞的、不可靠的或不可移植的代碼。沒有棄權(quán)就不能違反要求或限制

            最根本的原則

            使用常識(shí)

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


            第二章

            代碼組織與風(fēng)格

            本章提供程序結(jié)構(gòu)與層次的指導(dǎo)。

            代碼結(jié)構(gòu)

            開發(fā)大型系統(tǒng)時(shí)通常將它分成許多小的功能子系統(tǒng)。子系統(tǒng)本身由許多代碼模塊組成。在 C++ 中,一個(gè)模塊通常包含了實(shí)現(xiàn)單一一個(gè),少數(shù)情況下,也可能是一組緊密相關(guān)的抽象。C++ 中,一個(gè)抽象通常實(shí)現(xiàn)為一個(gè)。一個(gè)類有兩個(gè)不同的構(gòu)件:一個(gè)是對(duì)類客戶可見的接口,它提供了類能力和責(zé)任的聲明或規(guī)約;另一個(gè)是所聲明規(guī)約(類定義)的實(shí)現(xiàn)
            與類相似,模塊也有它的接口和實(shí)現(xiàn):模塊接口包括對(duì)所包含模塊抽象(類聲明)的規(guī)約;模塊實(shí)現(xiàn)包括對(duì)抽象(類定義)的實(shí)際實(shí)現(xiàn)。
            在系統(tǒng)構(gòu)造時(shí),可將子系統(tǒng)組織成協(xié)作的組或?qū)觼頊p少或控制它們間的依賴性。

            不同文件中放置模塊規(guī)約與實(shí)現(xiàn)

            模塊的規(guī)約應(yīng)放置在與模塊實(shí)施文件不同的文件中,此文件指頭文件。模塊的實(shí)施文件可能放置于一個(gè)或多個(gè)實(shí)施文件中。
            如果模塊實(shí)現(xiàn)包括擴(kuò)展內(nèi)嵌函數(shù)、普通私有實(shí)現(xiàn)聲明、測(cè)試代碼或具體平臺(tái)的代碼,那就把這些部分分開存儲(chǔ),并以那部分內(nèi)容來命名。
            如果程序的可執(zhí)行大小是一個(gè)要考慮的問題,則不常用到的函數(shù)應(yīng)當(dāng)置于它們各自的文件中。
            以以下方式構(gòu)造一部分文件名:

            • 以模塊的主要抽象名作為模塊名。
            • 為模塊名附加一部分類型名。選擇暗示它們類型的部分類型名。
            • 模塊名和部分名由分隔符分隔(如“_”(下劃線)或“.”(句點(diǎn)));選擇一個(gè)分隔符,使用它要前后一致。

              File_Name::=

              <Module_Name> [<Separator> <Part_Name>] '.'<File_Extension>

            • 為了更好的預(yù)測(cè),對(duì)文件名使用相同的大小寫,這與代碼內(nèi)名稱的使用相同。

            以下是模塊劃分與命名策略的示例:

            • module.inlines.cc - 如果一個(gè)模塊有多個(gè)潛在的內(nèi)嵌函數(shù),就將函數(shù)的定義置于一個(gè)單獨(dú)的內(nèi)嵌文件中(請(qǐng)參見“將模塊內(nèi)嵌函數(shù)定義置于單獨(dú)文件中”)。
            • module.private.hh - 如果模塊有許多常用的被其他部分引用的私有實(shí)現(xiàn)聲明,就把這些聲明分隔出去組成一個(gè)私有部分,它可由其他實(shí)施文件包含。
            • module.private.cc - 模塊的私有實(shí)施函數(shù)定義,為編輯的方便將它分離出去。
            • module.function_name.cc - 如果可執(zhí)行的大小是個(gè)要考慮的問題,應(yīng)當(dāng)分離出許多程序不需要的特殊成員函數(shù),組成各自的實(shí)施文件(請(qǐng)參見“如果程序大小需要考慮,將大型模塊分成多個(gè)變換單元”)。如果重載的函數(shù)置于不同文件中,每一文件函數(shù)名都應(yīng)有一實(shí)例數(shù)字的后綴。如,function_name1 表示第一個(gè)獨(dú)立重載函數(shù)實(shí)例。
            • module.nested_class.cc - 模塊嵌套類的成員函數(shù),置于其本身文件中。
            • module.test.[hh\cc] - 如果一個(gè)模塊需要擴(kuò)展測(cè)試代碼,則測(cè)試代碼必須在友測(cè)試類中予以聲明。友測(cè)試類應(yīng)稱為Module.Test。將測(cè)試代碼聲明為友類有助于模塊及其測(cè)試代碼的獨(dú)立開發(fā);這還允許測(cè)試代碼從最終模塊對(duì)象代碼中刪除而源并不改變。
            • module.platform_name.cc - 分離出任意模塊的平臺(tái)依賴性并以平臺(tái)名稱命名部分名稱(請(qǐng)參見“分離平臺(tái)依賴性”)。

            選擇一個(gè)模塊劃分和命名的機(jī)制,使用它要前后一致。

            示例
            SymaNetwork.hh         //包括類
                                   // “SymaNetwork”的聲明。
            SymaNetwork.Inlines.cc //內(nèi)嵌定義子單元
            SymaNetwork.cc         //模塊的主要實(shí)施單元
            SymaNetwork.Private.cc //私有實(shí)施子單元
            SymaNetwork.Test.cc    //測(cè)試代碼子單元
            基本原理

            從模塊的實(shí)施中分離出其規(guī)約有助于用戶和提供者代碼的獨(dú)立開發(fā)。
            將一個(gè)模塊的實(shí)施分割成多個(gè)變換單元,這可以更好的支持刪除對(duì)象代碼,并會(huì)導(dǎo)致更小的可執(zhí)行大小。
            使用規(guī)范化的文件命名與劃分約定允許在不檢查模塊的實(shí)際內(nèi)容時(shí)理解其內(nèi)容與組織。
            將名稱從代碼讓渡給文件名增加了可預(yù)測(cè)性,有助于建立基于文件的工具而不需要復(fù)雜命名映射 [Ellemtel, 1993]

            只選擇一組文件擴(kuò)展名以區(qū)分頭文件和實(shí)施文件

            常用的文件擴(kuò)展名是:對(duì)頭文件,為.h、.H、.hh、.hpp.hxx;對(duì)實(shí)施文件,為.c、.C、.cc、.cpp和.cxx。選擇一組擴(kuò)展名,使用它要前后一致。

            示例
            SymaNetwork.hh  //擴(kuò)展名“.hh”用來指定
                            //一個(gè)“SymaNetwork”模塊的頭文件。
            SymaNetwork.cc  //擴(kuò)展名“.cc”用來指定
                            //一個(gè)“SymaNetwork”模塊的實(shí)施文件
            注意

            對(duì)由名字空間封裝的頭文件,C++ 草擬標(biāo)準(zhǔn)工作底稿也使用擴(kuò)展名“.ns”。

            每個(gè)模塊規(guī)約避免定義多個(gè)類


            只有在極少數(shù)情況下多個(gè)類才能置于同一個(gè)模塊中;此時(shí)他們必須是緊密關(guān)聯(lián)關(guān)系的(如容器與它的迭代程序)如果所有類都需要對(duì)客戶模塊是可見的,可以把模塊的主要類及其支持類置于同一個(gè)頭文件中。

            基本原理

            減少模塊的接口以及其他模塊對(duì)此接口的依賴。

            避免將私有實(shí)施聲明置于模塊的規(guī)約中

            除了類的私有成員以外,模塊的私有實(shí)施聲明(如實(shí)施類型和支持類)不能出現(xiàn)在模塊的規(guī)約中。除非多個(gè)實(shí)施文件都需要這些聲明,否則它們應(yīng)當(dāng)置于所需的實(shí)施文件中;如果有多個(gè)實(shí)施文件,則這些聲明應(yīng)置于輔助私有頭文件中。而其他實(shí)施文件在需要時(shí)應(yīng)包含這個(gè)輔助私有頭文件。
            這一做法保證了:
            模塊規(guī)約清楚的表示了它的抽象,不再需要實(shí)施工件;
            模塊規(guī)約越小越好,并因此減小了模塊間的編譯依賴(請(qǐng)參見“減小編譯依賴”);

            示例
            //模塊 foo 的規(guī)約,包含于文件“foo.hh”中;
            //
            class foo
            {
            .. 聲明
            };
            //“foo.hh”結(jié)束;
            //模塊 foo 的私有聲明,包含于文件
            // “foo.private.hh”中;所有 foo 實(shí)施文件都使用它。
            ... 私有聲明
            //“foo.private.hh”結(jié)束;
            //模塊 foo 的實(shí)施,包含于以下多個(gè)文件中
            // “foo.x.cc”和“foo.y.cc”
            //文件“foo.x.cc”;
            //
            #include "foo.hh" //包含模塊本身的頭文件
            #include "foo.private.hh" //包含實(shí)施
            //所需的聲明。
            ... 定義
            //“foo.x.cc”結(jié)束
            //文件“foo.y.cc”;
            //
            #include "foo.hh"
            #include "foo.private.hh"
            ... 定義
            //“foo.y.cc”結(jié)束

            通常使用 #include 來訪問模塊的規(guī)約

            模塊要使用另一模塊,則必須使用預(yù)處理 #include 指令來訪問提供者模塊的規(guī)約。相應(yīng)的,模塊不能再次聲明提供者模塊規(guī)約的任何一部分。
            當(dāng)包含文件時(shí),只有對(duì)“標(biāo)準(zhǔn)”頭文件使用 #include <header> 語法;對(duì)其余的使用 #include "header" 語法。
            使用 #include 指令也適用于模塊自身的實(shí)施文件:模塊實(shí)施文件必須包括它本身的規(guī)約和私有輔助頭文件(請(qǐng)參見“不同文件中放置模塊規(guī)約與實(shí)施”);

            示例
            //模塊 foo 頭文件中的規(guī)約
            // "foo.hh"
            //
            class foo
            {
            ... 聲明
            };
            //“foo.hh”結(jié)束;
            
            //模塊 foo 在文件“foo.cc”中的實(shí)施;
            //
            #include "foo.hh" // 實(shí)施文件包含
            //它本身的規(guī)約
            ... foo 成員的定義
            //“foo.cc”結(jié)束

            #include 規(guī)則的一個(gè)例外是當(dāng)模塊只通過引用(使用指針或引用類型聲明)使用或包含提供者模塊的類型(類);這種情況下引用或包含通過使用預(yù)先聲明來規(guī)約(請(qǐng)參見“減小編譯依賴”),而不使用 #include 指令。
            只包含絕對(duì)需要的文件:這意味著模塊頭文件不應(yīng)包含只有模塊實(shí)施需要的其他頭文件。

            示例
            #include "a_supplier.hh"
            
            class needed_only_by_reference;//對(duì)只需要
            		            //指針或引用的類
                                          //使用
                                          //預(yù)先聲明來訪問
            void operation_requiring_object(a_supplier required_supplier, ...);
            //
            //操作需要一個(gè)實(shí)際的提供者對(duì)象;
            //提供者規(guī)約必須已有 #include 指令。
            
            void some_operation(needed_only_by_reference& a_reference, ...);
            //
            //某些操作只需要對(duì)對(duì)象進(jìn)行引用;
            //因此為提供者使用預(yù)先聲明。
            基本原理

            這一規(guī)則保證了:

            • 只存在唯一的模塊接口聲明,所有的客戶只看到同一接口;
            • 模塊間的編譯依賴最大程度的減小了;
            • 客戶不會(huì)為代碼無故產(chǎn)生不需要的編譯開支。

            將模塊內(nèi)嵌函數(shù)的定義置于單獨(dú)的文件中

            當(dāng)模塊有許多內(nèi)嵌函數(shù)時(shí),它們的定義應(yīng)置于一個(gè)分離的只有內(nèi)嵌函數(shù)的文件中。在模塊頭文件的末尾應(yīng)包含內(nèi)嵌函數(shù)文件。
            請(qǐng)參見“使用 No_Inline 條件編譯符破壞內(nèi)嵌編譯”。

            基本原理

            這種技術(shù)使實(shí)施細(xì)節(jié)不會(huì)聚集到模塊頭文件中;因此,保留了一個(gè)清晰的規(guī)約。當(dāng)不進(jìn)內(nèi)嵌編譯時(shí),它也有助于減少代碼復(fù)制:使用條件編譯,可將內(nèi)嵌函數(shù)編譯成一個(gè)對(duì)象文件而不是靜態(tài)的編譯成每一個(gè)使用的模塊。相應(yīng)的,內(nèi)嵌函數(shù)定義不應(yīng)在類定義中定義,除非它們非常瑣碎。

            如果程序規(guī)模是個(gè)要求考慮的問題,就把大的模塊分割成多個(gè)變換單元

            將大型模塊分割成多個(gè)變換單元有助于在程序鏈接中刪除未經(jīng)引用的代碼。很少引用的成員函數(shù)應(yīng)當(dāng)分割成獨(dú)立文件,與經(jīng)常使用的函數(shù)相分離。最后,各成員函數(shù)可置于他們自己的文件中 [Ellemtel, 1993]

            基本原理

            鏈接器在對(duì)象文件中消除無引用代碼的能力各不相同。將大型模塊分割成多個(gè)文件允許這些鏈接器通過消除整個(gè)對(duì)象文件中的鏈接來減少可執(zhí)行大小 [Ellemtel, 1993]

            注意

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

            分離平臺(tái)依賴性

            從獨(dú)立于平臺(tái)的代碼中分離出依賴于平臺(tái)的代碼;這有助于提高可移植性。依賴于平臺(tái)的模塊應(yīng)當(dāng)具有受平臺(tái)名限制的文件名以突出對(duì)平臺(tái)的依賴。

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

            從構(gòu)架與維護(hù)角度,將對(duì)平臺(tái)的依賴包含在少數(shù)幾個(gè)低層子系統(tǒng)中也是一個(gè)好方法。
            采納一個(gè)標(biāo)準(zhǔn)文件內(nèi)容結(jié)構(gòu),使用它要前后一致。
            建議文件內(nèi)容就構(gòu)包括以下次序的以下部分:

              1. 重復(fù)包含保護(hù)(只適用于規(guī)約)。
              2. 確定可選文件與版本控制。
              3. 本單元所需包含的文件。
              4. 模塊紀(jì)錄(只適用于規(guī)約)。
              5. 聲明(類、類型、常量、對(duì)象、函數(shù))和其他文本規(guī)約(前置及后置條件,常量)。
              6. 對(duì)這種模塊內(nèi)嵌函數(shù)定義的包含。
              7. 定義(對(duì)象與函數(shù))和私有實(shí)施聲明。
              8. 版權(quán)聲明。
              9. 可選版本控制歷史。

            基本原理

            以上文件的內(nèi)容次序,首先呈現(xiàn)給了客戶相關(guān)的信息;這與安排類的公共、保護(hù)和私有部分次序的基本原理相一致。

            注意

            根據(jù)公司的政策,版權(quán)信息應(yīng)置于文件的首部。

            防止重復(fù)文件包含的保護(hù)

            通過在每個(gè)頭文件中使用以下結(jié)構(gòu)可以防止重復(fù)包含與編譯文件:

            #if !defined(module_name) //使用預(yù)處理符
            #define module_name       //防止重復(fù)
                                      //包含...//聲明到此
            #include "module_name.inlines.cc" //可選的內(nèi)嵌結(jié)構(gòu)
                                              //包含到此。
            //包含模塊內(nèi)嵌函數(shù)之后
            //沒有其他聲明。
            #endif //module_name.hh 結(jié)束

            對(duì)包含保護(hù)符使用模塊文件名。對(duì)模塊名與操作符使用同一大小寫。

            使用"No_Inline"條件編譯符破壞內(nèi)嵌編譯

            使用以下條件編譯結(jié)構(gòu)控制可內(nèi)嵌函數(shù)的內(nèi)嵌(相對(duì)于外部的)編譯。

            //在文件 module_name.inlines.hh 首部,
            #if !defined(module_name_inlines)
            #define module_name_inlines
            
            #if defined(No_Inline)
            #define inline //使內(nèi)嵌關(guān)鍵詞無效
            #endif
            
            ... //內(nèi)嵌定義到此
            #endif //文件 module_name.inlines.hh 結(jié)束
            
            //文件 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

            條件編譯結(jié)構(gòu)與多包含保護(hù)結(jié)構(gòu)相似。如果未定義 No_Inline,則內(nèi)嵌函數(shù)與模塊規(guī)約一同編譯并自動(dòng)從模塊實(shí)施中排除出去。如果定義了 No_Inline,則模塊規(guī)約不包含內(nèi)嵌定義,但廢除 inline 關(guān)鍵詞的模塊實(shí)施包含了它。

            基本原理

            以上技術(shù)允許內(nèi)嵌函數(shù)在外部編譯時(shí)的精簡(jiǎn)代碼復(fù)制。使用條件編譯,內(nèi)嵌函數(shù)的簡(jiǎn)單復(fù)制編譯成了定義模塊;而當(dāng)由編譯器開關(guān)規(guī)約外部編譯時(shí),所復(fù)制的代碼在每個(gè)使用的模塊里編譯成“靜態(tài)”(內(nèi)部鏈接)函數(shù)。

            注意

            條件編譯增加了維護(hù)構(gòu)建依賴關(guān)系的復(fù)雜性。對(duì)這種復(fù)雜性的管理通常是利用把頭文件和內(nèi)嵌函數(shù)定義視為一個(gè)邏輯單元:實(shí)施文件因此依賴于頭文件以及內(nèi)嵌函數(shù)定義文件。

            代碼風(fēng)格

            對(duì)嵌套語句使用小的、一致的縮進(jìn)風(fēng)格

            使用一致縮進(jìn)來清楚的界定嵌套語句;為達(dá)到這一目的,兩到四個(gè)空格的縮進(jìn)是最有效的方式。我們推薦使用規(guī)則的兩空格縮進(jìn)。
            混合語句或塊語句的分界符 ({}),應(yīng)當(dāng)與周圍的語句處于縮進(jìn)的同一級(jí)上(這意味著垂直布置{})。通過選擇空格的數(shù)量對(duì)塊內(nèi)的語句進(jìn)行縮進(jìn)。
            switch 語句的 case 標(biāo)號(hào)應(yīng)當(dāng)與 switch 語句處于同一縮進(jìn)級(jí);switch 語句內(nèi)語句應(yīng)比 switch 語句本身和 case 標(biāo)號(hào)多縮進(jìn)一級(jí)。

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

            能夠輕易識(shí)別程序塊,還要在不超出顯示屏或打印頁(yè)面界限的前提下有足夠多的嵌套,使用兩空格的縮進(jìn)是一個(gè)折衷的方案。

            相對(duì)于函數(shù)名或作用域名縮進(jìn)函數(shù)參數(shù)

            如果一行之內(nèi)難以容下函數(shù)聲明,則將函數(shù)的第一個(gè)參數(shù)放在函數(shù)名所在的那一行;其余參數(shù)每個(gè)占一新行,使用與第一個(gè)參數(shù)相同的縮進(jìn)。下面所示的聲明與縮進(jìn)的風(fēng)格,在函數(shù)返回類型和名稱下留有空格;因此,提高了它們的可見性。

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

            如果按照以上的指南會(huì)導(dǎo)致?lián)Q行,或參數(shù)縮進(jìn)的太多,則以函數(shù)名或作用域名(類、名字空間)縮進(jìn)所有的參數(shù),每一參數(shù)獨(dú)占一行:

            示例
            void foo::function_with_a_long_name( //函數(shù)名是
                                                 //不易看到的
                                                 some_type first_parameter, 
                                                 some_other_type second_parameter,
                                                 status_type and_subsequent);

            請(qǐng)參照下面的布置規(guī)則。

            使用能夠適合標(biāo)準(zhǔn)打印紙大小的最大行寬

            應(yīng)限制程序行的寬度以防止在使用標(biāo)準(zhǔn)(信紙)或缺省打印紙張進(jìn)行打印時(shí)丟失信息。

            注意

            如果縮進(jìn)使深層的嵌套語句處于太右邊,語句又太長(zhǎng)以致超出了右邊的空白,這時(shí)就應(yīng)當(dāng)考慮將代碼分成更小的更易管理的函數(shù)。

            使用一致?lián)Q行

            當(dāng)函數(shù)聲明、定義、調(diào)用或 enum 聲明中枚舉操作符的參數(shù)列表不能置于一行,則將每一個(gè)列表元素置于單獨(dú)的一行(請(qǐng)參見“相對(duì)函數(shù)名和作用域縮進(jìn)函數(shù)參數(shù)”)。

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

            如果一個(gè)類或函數(shù)模板聲明太長(zhǎng),則在模板的實(shí)參列表后進(jìn)行連續(xù)換行。例如(標(biāo)準(zhǔn)迭代程序庫(kù)的聲明,[X3J16, 95]):

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



            第三章

            注釋

            本章對(duì)代碼注釋的使用提供指導(dǎo)。
            注釋應(yīng)當(dāng)作為源代碼的補(bǔ)充,而不是直譯源代碼:

            • 它們應(yīng)當(dāng)解釋不能直接從源代碼看出東西;它們不應(yīng)復(fù)制語言的語法或語義。
            • 它們應(yīng)當(dāng)幫助讀者掌握背景中的概念、依賴性、特別是復(fù)雜的數(shù)據(jù)代碼和算法。
            • 它們應(yīng)當(dāng)突出:與代碼或設(shè)計(jì)標(biāo)準(zhǔn)的不同點(diǎn)、受限特性的使用、以及特殊的“技巧”。

            對(duì)每一行注釋,程序員都應(yīng)能夠輕松回答:這一注釋有何價(jià)值?通常,合理選擇名稱就可不要注釋。除非注釋出現(xiàn)在正規(guī)的程序設(shè)計(jì)語言中 (PDL),否則編譯器不對(duì)其進(jìn)行編譯;因此,根據(jù)單點(diǎn)維護(hù)原則,應(yīng)當(dāng)以源代碼的方式表達(dá)設(shè)計(jì)決策,而不是以注釋的方式,即使這樣需要更多的聲明。

            使用 C++ 風(fēng)格的注釋而非 C 風(fēng)格的注釋

            應(yīng)使用 C++ 風(fēng)格注釋分界符"//",而非 C 風(fēng)格的"/*...*/"。

            基本原理

            C++ 風(fēng)格的注釋更易理解,它減少了由于偶然缺少一個(gè)注釋結(jié)束分隔符而造成大量代碼被注釋掉的風(fēng)險(xiǎn)。

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

            注釋與源代碼盡可能靠攏

            注釋應(yīng)緊貼它們所要注釋的代碼;它們使用相同的縮進(jìn),使用一個(gè)空注釋行接于代碼之后。
            對(duì)多行連續(xù)語句的注釋應(yīng)置于語句的上方作為語句的介紹性說明。而對(duì)單個(gè)語句的注釋應(yīng)置于語句的下方

            示例
            //置于語句前的注釋
            //它對(duì)下面多個(gè)語句進(jìn)行注釋
            // 
            ...
            void function();
            //
            //語句之后的注釋
            //它對(duì)前一個(gè)語句進(jìn)行注釋。

            避免行末注釋

            注釋應(yīng)避免與源結(jié)構(gòu)處于同一行:否則會(huì)使注釋與其所注釋的源代碼不對(duì)齊。但在描述長(zhǎng)聲明中的元素(如 enum 聲明中的枚舉操作符)時(shí),也能容忍這種不好的注釋方式。

            避免注釋頭

            避免使用包含作者、電話號(hào)碼、創(chuàng)建和修改日期的注釋頭:作者和電話號(hào)碼很快就過時(shí)了;而創(chuàng)建和修改日期以及修改的原因則最好由配置管理工具來維護(hù)(或其他形式的版本歷史文件)。
            即使對(duì)主結(jié)構(gòu)(如函數(shù)和類),也要避免使用垂直滾動(dòng)條,閉合的欄或框。它們搞亂了視覺效果,這樣就很難保持一致性。使用空行而不是多個(gè)注釋行來分割相關(guān)的源代碼塊。使用一個(gè)空行來分離函數(shù)或類中的結(jié)構(gòu)。使用兩個(gè)空行將函數(shù)與函數(shù)相分離。
            框架或表單看起來具有較好的一致性,還提醒了程序員來注釋代碼,但它們會(huì)導(dǎo)致直譯的風(fēng)格 [Kruchten, 94]

            使用空注釋行分離注釋段

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

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

            避免冗余

            注釋中應(yīng)避免重復(fù)程序標(biāo)識(shí)符,避免復(fù)制別處有的信息(此時(shí)可使用一個(gè)指向信息的指針)。否則程序中的任何一處改動(dòng)都可能需要多處進(jìn)行相應(yīng)的變動(dòng)。如果其他地方?jīng)]有進(jìn)行所需的注釋改動(dòng),將會(huì)導(dǎo)致誤注釋:這種結(jié)果比根本沒有注釋還要糟糕。

            編寫自記錄代碼而非注釋

            時(shí)刻注意要編寫自記錄代碼而非注釋。這可通過選擇合理的名稱、使用特殊的臨時(shí)變量或重新安排代碼的結(jié)構(gòu)來實(shí)施。注意注釋中的風(fēng)格、語法和拼寫。使用自然語言注釋而不是電報(bào)或加密格式。

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

            記錄類與函數(shù)

            雖然自記錄代碼比注釋好;但通常還需要提供一些超出解釋代碼復(fù)雜部分以外的信息。至少需要記錄以下信息:

            • 每個(gè)類的目的;
            • 函數(shù)名不能清楚說明它的目的時(shí),則記錄函數(shù)相應(yīng)的目的;
            • 返回值的含義;如不可預(yù)測(cè)函數(shù)布爾返回值的含義:如,ture 值是否表示函數(shù)執(zhí)行成功;
            • 出現(xiàn)異常的條件;
            • 如果有的話,參數(shù)的前置或后置條件;
            • 其他數(shù)據(jù)訪問,特別是當(dāng)數(shù)據(jù)被修改時(shí):對(duì)有副作用的函數(shù)特別重要;
            • 合理使用類或函數(shù)所需的限制或其他信息;
            • 對(duì)類型和對(duì)象來說,語言無法表達(dá)的任何不變量或其他限制。
            基本原理

            與聲明相結(jié)合的代碼記錄應(yīng)當(dāng)足以使客戶使用代碼;因?yàn)橹皇褂?C++ 不能完全表達(dá)類、函數(shù)、類型和對(duì)象的完整語義,所以需要記錄存檔。


            第四章

            命名

            本章為不同 C++ 實(shí)體命名指南。

            概述

            為程序?qū)嶓w(類、函數(shù)、類型、對(duì)象、常量、異常、名字空間)取一個(gè)好名稱并不是一件容易的事。對(duì)中大型的應(yīng)用程序來說,問題就顯得更加困難:這里名稱容易沖突,并且還缺少一些近義詞來區(qū)分相似但不相同的概念,這就更增加了困難的程度。
            使用名稱約定可減少創(chuàng)造合適名稱時(shí)的腦力勞動(dòng)。除了這一好處以外,名稱約定還帶來了代碼的一致性。一個(gè)有用的名稱約定必須在以下方面提供向?qū)В号虐骘L(fēng)格(或如何書寫名稱)以及名稱的構(gòu)建(或如何選擇名稱)。

            選擇一個(gè)名稱約定,使用它要前后一致

            只要能夠前后一致,使用哪一個(gè)名稱約定并不重要。命名的一致性比實(shí)際的約定重要的多:一致性支持“最小混淆”原則。
            因?yàn)?C++ 是一個(gè)區(qū)分大小寫的語言,并且在 C++ 應(yīng)用界有多種不同的命名約定,幾乎不可能實(shí)現(xiàn)命名的絕對(duì)一致。我們推薦基于主機(jī)的環(huán)境(如 UNIX 或 Windows)以及項(xiàng)目所使用的主要的庫(kù)選擇項(xiàng)目的命名約定;盡可能實(shí)現(xiàn)代碼的一致性:

            • 主機(jī)使用 UNIX 且不常使用商業(yè)庫(kù)(如 X Window 庫(kù)、X Toolkit Intrinsics 和 Motif)的項(xiàng)目可能傾向于使用全部為小寫字符、以下劃線分隔的約定:這是 UNIX 系統(tǒng)調(diào)用以及 C++ 草擬標(biāo)準(zhǔn)工作底稿使用的約定。
            • 以商業(yè)庫(kù)為中心的 UNIX 主機(jī)項(xiàng)目可能更傾向于使用大寫風(fēng)格,通常也稱為 Smalltalk 風(fēng)格 - 一種首字母大寫,字間直接相連而無分隔符的書寫風(fēng)格。
            • Microsoft 基于 Windows 的項(xiàng)目可能會(huì)推薦使用并不常用的 Microsoft? “匈牙利”命名法我們不推薦使用這種命名風(fēng)格,因?yàn)樗`背了本文指南的基本原則。
            注意:

            細(xì)心的讀者會(huì)發(fā)現(xiàn)本文的示例現(xiàn)在并未遵循所有的指南。這部分是因?yàn)槭纠膩碓从卸鄠€(gè),還因?yàn)橄M麥p少篇幅。因此,沒有嚴(yán)格遵照格式化的指南來做。但需聲明:照我說的去做,不要學(xué)我的實(shí)際做法。

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

            開頭帶有一個(gè)下劃線(“_”)的名稱通常由庫(kù)函數(shù)(“_main”和“_exit”)使用。開頭帶有兩個(gè)下劃線(“__”)或一個(gè)下劃線后接一個(gè)大寫字母的名稱保留給編譯器內(nèi)部使用。
            名稱還要避免下劃線相鄰,否則很難識(shí)別下劃線的確切個(gè)數(shù)。

            避免使用只靠字母大小寫才能區(qū)分的名稱

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

            避免使用縮寫

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

            避免使用后綴來表明程序語言結(jié)構(gòu)

            使用后綴劃分實(shí)體種類(如對(duì)type(類型)使用 type,對(duì) exceptions(異常)使用 error)對(duì)幫助理解代碼通常并不有效。后綴如 arraystruct 還意味著一個(gè)具體的實(shí)現(xiàn);在實(shí)現(xiàn)改變時(shí)(結(jié)構(gòu)或數(shù)組表示的改變)對(duì)客戶代碼要么會(huì)有反面的結(jié)果,要么會(huì)起誤導(dǎo)的作用。
            但后綴在有限幾種情況下也會(huì)有一定用處:

            • 當(dāng)可供選擇的標(biāo)識(shí)符非常有限時(shí),選擇一個(gè)最佳名稱并用后綴表明它的類型。
            • 當(dāng)它表示一個(gè)應(yīng)用領(lǐng)域的概念如 aircraft_type(飛行器類型)時(shí)。

            選擇清晰的、易辨認(rèn)的、有意義的名稱

            從應(yīng)用的角度選擇名稱,名詞使用形容詞修飾來提高局部(具體上下文)的含義。確保名稱與其類型保持一致。
            選擇合適的名稱,使以下結(jié)構(gòu):

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

            易于閱讀并有實(shí)際含義。
            短名稱或縮寫名稱打字速度快不是使用它們的可以接受的理由。單字母或很短幾個(gè)字母的標(biāo)識(shí)符通常出于選擇不當(dāng)或懶惰。但使用人們熟知的 E 作為自然對(duì)數(shù)的底數(shù)或 Pi 作為圓周率就是例外了。
            不幸的是,編譯器和支持的工具有時(shí)限制名稱的長(zhǎng)度。因此,應(yīng)當(dāng)注意:長(zhǎng)名稱不應(yīng)只在結(jié)尾的幾個(gè)字符有所不同,因?yàn)榻Y(jié)尾的幾個(gè)用于區(qū)分的字符可能會(huì)被這些工具截?cái)唷?/p>

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

            第一個(gè)示例中名稱的選擇要優(yōu)于其它兩個(gè):對(duì) new_color 進(jìn)行了限定使它與其類型一致;因此增強(qiáng)了函數(shù)的語義。
            第二個(gè)示例中,讀者直覺上會(huì)認(rèn)為 fg 意指 foreground(前景);但是,任何一個(gè)好的編程風(fēng)格都不應(yīng)留給讀者作直覺推導(dǎo)的余地。
            第三個(gè)示例中,當(dāng)使用參數(shù) foreground(遠(yuǎn)離其聲明)時(shí),讀者會(huì)認(rèn)為 foreground 實(shí)際上就是指 foreground 的顏色。可以想象,它能代表任何一種可暗中轉(zhuǎn)化為 color 的類型。

            注意:

            使用名詞和形容詞構(gòu)成名稱、確保名稱與類型相符、遵循自然語言以增強(qiáng)代碼的可讀性和語義。

            使用名稱的正確拼寫

            英語字符名稱部分應(yīng)正確的拼寫,遵守項(xiàng)目要求的形式,如使用一致的英國(guó)英語或美國(guó)英語,但不能同時(shí)使用。這對(duì)注釋也同樣適用。

            布爾值使用正值謂詞從句

            對(duì)布爾值類型的對(duì)象、函數(shù)及函數(shù)實(shí)參使用正值形式的判定句式,如 found_itis_available,但不使用 is_not_available

            基本原理

            當(dāng)否定謂詞時(shí),雙重否定更難以理解。

            名字空間

            使用名字空間由子系統(tǒng)或庫(kù)劃分潛在全局名稱

            如果將系統(tǒng)解構(gòu)成子系統(tǒng),使用子系統(tǒng)名稱作為名字空間的名稱,這樣可劃分系統(tǒng)的全局名字空間并使其最小化。如果系統(tǒng)是一個(gè)庫(kù),對(duì)整個(gè)庫(kù)使用一個(gè)最外層的名字空間。
            為每一個(gè)子系統(tǒng)或庫(kù)名字空間取一個(gè)有意義的名稱;此外,為它取一個(gè)簡(jiǎn)化的或縮寫的別名。選擇不會(huì)引起沖突的簡(jiǎn)寫或縮寫的別名,如 ANSI C++ draft standard library(ANSI C++ 草擬標(biāo)準(zhǔn)庫(kù))[Plauger, 95]std 定義為 iso_standard_library 的別名。
            如果編譯器尚未支持名字空間結(jié)構(gòu),使用名稱前綴模擬名字空間。例如,系統(tǒng)管理子系統(tǒng)接口中的公共名稱應(yīng)帶有前綴 syma (System Management(系統(tǒng)管理)的簡(jiǎn)寫)。

            基本原理

            使用名字空間包含可能的全局名稱,這樣有助于子項(xiàng)目團(tuán)隊(duì)或廠商獨(dú)立開發(fā)代碼時(shí)避免名稱沖突。這必然導(dǎo)致只有名字空間是全局的。

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

            使用簡(jiǎn)單形式的常用名詞或名詞短語,為類取名時(shí)要表達(dá)出它的抽象含義。基類使用更通用的名稱,而對(duì)派生類使用更專用的名稱。

            typedef ... reference; //出自標(biāo)準(zhǔn)庫(kù)
            typedef ... pointer;   //出自標(biāo)準(zhǔn)庫(kù)
            typedef ... iterator;  //出自標(biāo)準(zhǔn)庫(kù)
            class bank_account {...};
            class savings_account : public bank_account {...};
            class checking_account : public bank_account {...};

            當(dāng)對(duì)象和類型的名稱沖突或缺少合適的名稱時(shí),對(duì)象使用簡(jiǎn)單名稱,類型名稱添加后綴 mode、kind、code 等。
            表明是對(duì)對(duì)象集合的抽象時(shí)使用復(fù)數(shù)形式。

            typedef some_container<...>yellow_pages;

            當(dāng)需要對(duì)象集合外的其他語義時(shí),使用以下標(biāo)準(zhǔn)庫(kù)中的數(shù)據(jù)結(jié)構(gòu)作為行為模式和名稱后綴:

            • vector(向量) - 一種可隨機(jī)訪問的順序容器;
            • list(表) - 一種有序的順序容器;
            • queue(隊(duì)列) - 一種先入先出的順序容器;
            • deque(雙端隊(duì)列) - 一種兩個(gè)端口都可進(jìn)行操作的隊(duì)列;
            • stack(堆棧) - 一種后入先出的順序容器;
            • set(集合) - 一種關(guān)鍵詞訪問(關(guān)聯(lián)關(guān)系)容器;
            • map(圖) - 一種關(guān)鍵詞訪問(關(guān)聯(lián)關(guān)系)容器;

            函數(shù)

            過程類型的函數(shù)名稱使用動(dòng)詞

            對(duì)無返回值的函數(shù)(函數(shù)聲明為 void 返回類型)或返回值使用指針或引用類型的函數(shù),使用動(dòng)詞或動(dòng)詞短語。
            對(duì)使用非 void 返回類型返回唯一一個(gè)值的函數(shù),使用名詞或名詞短語。
            對(duì)帶有常用操作(行為模式)的類,使用項(xiàng)目選項(xiàng)列表中的操作名。例如:begin, end, insert, erase (標(biāo)準(zhǔn)庫(kù)中的容器操作)。
            避免使用“get”和“set”來命名表明狀態(tài)的函數(shù)(給函數(shù)添加前綴“get”和“set”),特別是對(duì)取出和設(shè)置對(duì)象屬性的公共操作更是這樣。操作命名應(yīng)當(dāng)停留在類抽象和服務(wù)提供一級(jí)上;取出和設(shè)置對(duì)象屬性是低層次的實(shí)現(xiàn)細(xì)節(jié),如果使它們公共化,則會(huì)降低封裝的完整性。
            返回布爾值(謂詞)的函數(shù)使用形容詞(或過去分詞)。謂詞經(jīng)常使用名詞前添加前綴 ishas 的形式使名稱表達(dá)一個(gè)肯定的聲明。當(dāng)對(duì)象、類型名或枚舉型常量也使用簡(jiǎn)單名稱時(shí),這也同樣非常有用。時(shí)態(tài)上要保持一致,力求準(zhǔn)確。

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

            不要使用否定意義的名稱,因?yàn)檫@會(huì)導(dǎo)致雙重否定表達(dá)式出現(xiàn)(如 !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);

            當(dāng)需要使用同一通用含義時(shí),使用函數(shù)的重載

            當(dāng)操作目的相同時(shí),使用重載而不使用同義詞;這盡量減少了系統(tǒng)中概念的數(shù)量和不同的操作,因此也就降低了整體的復(fù)雜性。
            當(dāng)重載操作符時(shí),要確保操作符的語義保留了下來;如果不能保留約定俗成的操作符含義,則為函數(shù)選用另一個(gè)名稱而不使用操作符重載方式。

            對(duì)象與函數(shù)參數(shù)

            實(shí)參名使用語法元素強(qiáng)調(diào)其含義

            為表明其唯一性或表明本實(shí)體是活動(dòng)的主要核心,在對(duì)象或參數(shù)名稱前加前綴“the”或“this”。要表明次要、臨時(shí)、輔助對(duì)象,則加前綴“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);

            異常


            異常名選用否定的含義

            只有在處理錯(cuò)誤時(shí)才必須使用異常,故使用名詞或名詞短語來清楚的表達(dá)一個(gè)否定的含義:

            overflow, threshold_exceeded, bad_initial_value

            異常名使用項(xiàng)目定義過的形容詞

            使用項(xiàng)目定義列表中以下詞的一個(gè) bad、incomplete、invalid、wrong、missingillegal 作為名稱的一部分,而不要機(jī)械的套用 errorexception,因?yàn)樗鼈儾⒉槐磉_(dá)具體的信息。

            其他事項(xiàng)

            浮點(diǎn)指數(shù)和十六進(jìn)制數(shù)使用大寫字母。

            浮點(diǎn)數(shù)中的字母“E”和十六進(jìn)制數(shù)從“A”到“F”應(yīng)當(dāng)一直保持大寫。


            第五章

            聲明

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

            名字空間

            C++ 語言中名字空間存在之前,管理名稱的作用域只有有限的幾種手段;因此,全局名字空間的使用過于泛濫,導(dǎo)致眾多的沖突,這使同一程序中難以同時(shí)使用一些庫(kù)。新的名字空間語言特征解決了全局名字空間的干擾問題。

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

            這意味著只有名字空間的名稱可以是全局的;所有其他的聲明都必須在某個(gè)名字空間的作用域內(nèi)。
            忽略了這一規(guī)則可能最終導(dǎo)致名稱沖突。

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

            邏輯上劃分非類功能類的類別,或作用域遠(yuǎn)大于類的功能(如庫(kù)或子系統(tǒng)),使用名字空間邏輯上統(tǒng)一的聲明(請(qǐng)參見“使用名字空間由系統(tǒng)或庫(kù)劃分潛在的全局名稱”)。
            名稱表達(dá)了功能的邏輯劃分。

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

            盡量不使用全局和名字空間范圍的數(shù)據(jù)。

            使用全局和名字空間范圍內(nèi)數(shù)據(jù)與封裝原則是背道而馳的。

            C++ 中類是基本的設(shè)計(jì)實(shí)施單元。使用它們紀(jì)錄域與設(shè)計(jì)的抽象,并作為實(shí)施抽象數(shù)據(jù)類型 (ADT) 的封裝機(jī)制。

            使用 class 而不是 struct 來實(shí)現(xiàn)抽象數(shù)據(jù)類型

            使用 class(類的關(guān)鍵字)而不是 struct 來實(shí)現(xiàn)一個(gè)表示抽象數(shù)據(jù)類型的類。
            使用 struct(類的關(guān)鍵字)來定義類似 C 中的舊式數(shù)據(jù)結(jié)構(gòu) (POD),尤其是在與 C 代碼程序接口時(shí)。
            雖然 classstruct 是等價(jià)的,可以互換,但 class 具有更好的默認(rèn)訪問控制 (private),這樣就提供了更好的封裝性能。

            基本原理

            區(qū)分 classstruct 的的一致做法引入了以上的語義差別,并且還超出了語言規(guī)則的范圍:class 是紀(jì)錄抽象與封裝最先考慮的結(jié)構(gòu);而 struct 代表一個(gè)純數(shù)據(jù)結(jié)構(gòu),它可以在混合編程語言程序中交換使用。

            以可訪問權(quán)限逐次降低的順序聲明類的成員

            類聲明中的訪問限定符應(yīng)以順序 public, protected, private 出現(xiàn)。

            基本原理

            以 public、protected、private 順序排列的成員聲明保證了類用戶最感興趣的信息置于最前列,因此減少了類用戶訪問不相關(guān)信息或?qū)崿F(xiàn)細(xì)節(jié)。

            抽象數(shù)據(jù)類型避免聲明公共或保護(hù)數(shù)據(jù)成員

            公共或保護(hù)數(shù)據(jù)成員的使用降低了類的封裝性并影響了系統(tǒng)抵抗變化的能力:公共數(shù)據(jù)成員將類的實(shí)現(xiàn)暴露給了它的用戶;保護(hù)數(shù)據(jù)成員將類的實(shí)現(xiàn)暴露給了從它繼承的類。類公共數(shù)據(jù)成員或保護(hù)數(shù)據(jù)成員的任何改變都會(huì)影響到用戶和繼承類。

            使用友元保留封裝性

            初次碰到時(shí),它好像是違反直覺的:友元關(guān)系將一個(gè)類的私有部分暴露給了它的友元,還如何實(shí)現(xiàn)封裝呢?當(dāng)類之間高度相互依賴,需要相互間的內(nèi)部信息時(shí),最好是賦予它們間友元關(guān)系而不是通過類的接口將其內(nèi)部細(xì)節(jié)暴露出來。
            不希望如同公共成員一樣暴露類的內(nèi)部細(xì)節(jié)以提供給類客戶訪問權(quán)。將保護(hù)成員暴露給潛在的繼承類,鼓勵(lì)了分級(jí)設(shè)計(jì),但這也是不希望的。友元關(guān)系有選擇性的賦予了對(duì)私有成員的訪問權(quán),而不用實(shí)現(xiàn)子類限制。因此再所需訪問之外保留了封裝。
            將友元關(guān)系賦予友元測(cè)試類很好的說明了友元關(guān)系保留了封裝特性。友元測(cè)試類通過訪問類的內(nèi)部可完成相應(yīng)的測(cè)試代碼,之后,友元測(cè)試類可從交付的代碼中刪除。因此,沒有丟失封裝,也沒有向可交付代碼增加代碼。

            避免在類聲明中定義函數(shù)

            類的聲明應(yīng)當(dāng)只包含函數(shù)的聲明,永遠(yuǎn)不要進(jìn)行函數(shù)定義(實(shí)現(xiàn))。

            基本原理

            類聲明中定義函數(shù)干擾了類對(duì)其實(shí)現(xiàn)細(xì)節(jié)的規(guī)約;使類的接口難以辨認(rèn)并難以閱讀;并增加了對(duì)編譯的依賴。
            類聲明中的函數(shù)定義也減少了對(duì)內(nèi)嵌函數(shù)的控制(請(qǐng)參照“使用 No_Inline 條件編譯符禁止內(nèi)嵌編譯”)。

            明確聲明構(gòu)造函數(shù)的類使用默認(rèn)構(gòu)造函數(shù)

            為了在數(shù)組中或任何 STL 容器中使用類,類必須提供公共的默認(rèn)構(gòu)造函數(shù)或允許編譯器生成一個(gè)構(gòu)造函數(shù)。

            注意:

            當(dāng)類有非靜態(tài)引用類型的數(shù)據(jù)成員時(shí)是以上規(guī)則的一個(gè)例外,這種情況下通常不可能創(chuàng)建一個(gè)有意義的默認(rèn)構(gòu)造函數(shù)。因此,使用對(duì)象數(shù)據(jù)成員的引用就是不可靠的。

            帶有指針類型數(shù)據(jù)成員的類要聲明其復(fù)制構(gòu)造函數(shù)和賦值操作符

            如果需要,并且沒有明確聲明時(shí),編譯器會(huì)為類暗中生成一個(gè)復(fù)制構(gòu)造函數(shù)和一個(gè)賦值操作符。編譯器定義了復(fù)制構(gòu)造函數(shù)和賦值操作符,在 Smalltalk 術(shù)語中這通常稱為“shallow-copy”(淺拷貝):明確的說,即按成員拷貝以及對(duì)指針按位拷貝。使用編譯器生成的復(fù)制構(gòu)造函數(shù)和默認(rèn)的賦值操作符肯定會(huì)造成內(nèi)存泄漏。

            示例
            //摘自 [Meyers, 92].
            void f()
            {
            String hello("Hello");//假設(shè) String 類型
                                  //由指向 char 型
                                  //數(shù)組的指針來實(shí)現(xiàn)。
            { //進(jìn)入新的域(程序塊)
            String world("World");
            world = hello;        //賦值語句使 world 丟失了
                                  //它最初指向的內(nèi)存單元
            }	//從域中退出時(shí)
            	//解構(gòu) world;
            	//此時(shí)也間接的解構(gòu)了 hello
            String hello2 = hello; //將已解構(gòu)過的 hello 賦給
                                   // hello2
            }      

            以上代碼中,儲(chǔ)存字符串“World”的內(nèi)存單元在賦值語句之后丟失了。結(jié)束內(nèi)部程序塊時(shí),銷毀了world;因此,由 hello 引用的內(nèi)存單元也丟失了。已經(jīng)解構(gòu)的 hello 賦給了 hello2

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

            以上代碼中,當(dāng)調(diào)用函數(shù) foo 使用實(shí)參 lost 時(shí),利用編譯器定義的復(fù)制構(gòu)造函數(shù)將 lost 復(fù)制到 foo 中。因?yàn)閺?fù)制 lost 時(shí)對(duì)指向“String that will be lost!”的指針進(jìn)行逐位復(fù)制,當(dāng)從 foo 中退出時(shí),對(duì) lost 的復(fù)制(即形參 bar,譯者注)將會(huì)被銷毀(假設(shè)析構(gòu)函數(shù)正確的釋放了內(nèi)存單元),同時(shí)存儲(chǔ)字符串“String that will be lost! ”的內(nèi)存單元也會(huì)被釋放。

            不要重復(fù)聲明構(gòu)造函數(shù)參數(shù)有默認(rèn)值

            示例
            //示例摘自 [X3J16, 95; section 12.8]
            class X {
            public:
            X(const X&, int);	// int 參數(shù)沒有
            				   //初始化
            				   //沒有用戶聲明的復(fù)制構(gòu)造函數(shù),因此
            				   //編譯器暗中聲明了一個(gè)。
            };
            //int 參數(shù)滯后的初始化
            //將構(gòu)造函數(shù)變成了復(fù)制構(gòu)造函數(shù)。
            //
            X::X(const X& x, int i = 0) { ...}      
            基本原理

            編譯器在類聲明中沒有發(fā)現(xiàn)“標(biāo)準(zhǔn)”的復(fù)制構(gòu)造函數(shù)時(shí)會(huì)暗中聲明一個(gè)復(fù)制構(gòu)造函數(shù)。但是,默認(rèn)參數(shù)滯后的初始化可能將構(gòu)造函數(shù)改變成復(fù)制構(gòu)造函數(shù):使用復(fù)制構(gòu)造函數(shù)時(shí)導(dǎo)致混淆。因?yàn)檫@種混淆,任何復(fù)制構(gòu)造函數(shù)的使用都因此而變?yōu)椴B(tài)的。[X3J16, 95; section 12.8].

            將析構(gòu)函數(shù)總是聲明為 virtual 類型

            除非明確將類的設(shè)計(jì)為不可繼承的,否則應(yīng)將析構(gòu)函數(shù)聲明為 virtual 類型。

            基本原理

            如果基類的析構(gòu)函數(shù)沒有聲明為 virtual 類型,則通過基類的指針或引用刪除派生類對(duì)象將導(dǎo)致不確定的行為。

            示例
            //為了簡(jiǎn)便起見,這里使用了不好的命名風(fēng)格
            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; //由于基類
            		    //的析構(gòu)函數(shù)沒有定義成 virtual 類型,
            	       //這里的行為是不確定的
            }      

            避免聲明太多的轉(zhuǎn)換操作符和單參數(shù)構(gòu)造函數(shù)

            使用限定符 explicit 可防止單參數(shù)構(gòu)造函數(shù)用于隱式轉(zhuǎn)換。

            不要重定義非虛函數(shù)

            非虛函數(shù)實(shí)現(xiàn)固定的行為,并且不希望專用于派生類。違反這一原則將導(dǎo)致不可預(yù)料的行為:同一對(duì)象不同時(shí)間可能表現(xiàn)不同的行為。
            非虛函數(shù)是靜態(tài)綁定的;以下示例中,對(duì)象函數(shù)的調(diào)用由變量(指向 A 或 B 的指針)的靜態(tài)類型控制,而不是對(duì)象的實(shí)際類型。

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

            謹(jǐn)慎使用非虛函數(shù)

            因?yàn)榉翘摵瘮?shù)通過限制特殊化(即重載,譯者注)和多態(tài)的使用來限制子類,聲明為非虛擬之前,必須注意確保操作對(duì)所有子類確實(shí)是固定不變的。

            使用初始化構(gòu)造函數(shù)而不是使用構(gòu)造函數(shù)中的賦值語句

            對(duì)象構(gòu)造時(shí)其狀態(tài)的初始化應(yīng)由初始化構(gòu)造函數(shù)(一個(gè)成員初始化列表)完成,而不是通過構(gòu)造函數(shù)內(nèi)的賦值操作符完成。

            示例
            代碼如下:
            class X 
            {
            public:
            X();
            private
            Y the_y;
            };
            X::X() : the_y(some_y_expression) { } 
            //
            // “the_y”由初始化構(gòu)造函數(shù)進(jìn)行初始化
            而不是如下進(jìn)行初始化
            X::X() { the_y = some_y_expression; }
            //
            // “the_y”由賦值操作符進(jìn)行初始化
            基本原理

            對(duì)象的構(gòu)造涉及到在執(zhí)行構(gòu)造函數(shù)體之前構(gòu)造所有的基類以及數(shù)據(jù)成員。使用初始化構(gòu)造函數(shù)只需要一個(gè)操作(由初值構(gòu)造),而在構(gòu)造函數(shù)體中初始化數(shù)據(jù)成員需要兩個(gè)操作(構(gòu)造以及賦值)。
            對(duì)有多重嵌套的類(類包含類,所包含的類又包含其他的類),多個(gè)操作(構(gòu)造+成員賦值)造成的性能的開支就十分重要了。

            初始化構(gòu)造函數(shù)不要調(diào)用成員函數(shù)

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

            當(dāng)所有基類的成員初始化完成之前如果初始化構(gòu)造函數(shù)直接或間接的調(diào)用了成員函數(shù),此操作的結(jié)果是不確定的。[X3J16, 95].

            注意構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用時(shí)的情況

            構(gòu)造函數(shù)中調(diào)用成員函數(shù)時(shí)要格外注意;即使調(diào)用的是虛函數(shù)時(shí)也要注意。執(zhí)行的將是在類或其基類的構(gòu)造函數(shù)、析構(gòu)函數(shù)中定義的操作。

            整形類常量使用 static const

            定義整形(整數(shù))類常量時(shí),使用 static const 數(shù)據(jù)成員,不要使用 #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 100
            class C {
            char buffer[BUFFER_SIZE];
            };      

            函數(shù)

            一定要明確聲明函數(shù)的返回值類型

            這可以防止編譯器發(fā)現(xiàn)函數(shù)未聲明返回值類型時(shí)所造成的模糊不清。

            函數(shù)聲明中要提供正規(guī)參數(shù)名稱

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

            函數(shù)要盡量只有一個(gè)返回點(diǎn)

            返回語句在程序體中自由放置與 goto 語句的情況類似,會(huì)造成代碼難以閱讀和維護(hù)。
            只有在少數(shù)幾種函數(shù)中才能容忍出現(xiàn)多個(gè)返回語句,此時(shí)可以同時(shí)看到所有的返回語句 return 并且代碼還有非常規(guī)則的結(jié)構(gòu):

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

            函數(shù)的返回類型為 void 時(shí)無返回語句。

            避免創(chuàng)建對(duì)全局有副作用的函數(shù)

            應(yīng)盡量避免創(chuàng)建對(duì)全局有副作用的函數(shù),因?yàn)樗赡芨淖儾⒉恢榈臄?shù)據(jù)而非它們內(nèi)部對(duì)象的狀態(tài),如全局和名字空間數(shù)據(jù)(另請(qǐng)參見“盡量不使用全局或名字空間范圍的數(shù)據(jù)”)但如果這是不可避免的,則應(yīng)明確記錄下所有的副作用以作為函數(shù)規(guī)約的一部分。
            只在所需的對(duì)象中傳遞參數(shù)增加了代碼的上下文無關(guān)性,并提高了它的強(qiáng)壯性和易理解性。

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

            從調(diào)用者的角度講,參數(shù)聲明的順序是非常重要的:

            • 首先以重要性遞減的順序定義非默認(rèn)參數(shù);
            • 再以被修改可能性遞減的順序定義有默認(rèn)值的參數(shù)。

            這種順序使用了參數(shù)默認(rèn)值的優(yōu)點(diǎn)來減少函數(shù)調(diào)用中實(shí)參的個(gè)數(shù)。

            避免聲明帶有不定參數(shù)個(gè)數(shù)的函數(shù)

            帶有不定參數(shù)個(gè)數(shù)的函數(shù)無法對(duì)其實(shí)參進(jìn)行類型檢查。

            避免重復(fù)聲明帶有默認(rèn)參數(shù)的函數(shù)

            函數(shù)的進(jìn)一步重復(fù)聲明中應(yīng)避免添加默認(rèn)值:遠(yuǎn)離先前聲明,一個(gè)函數(shù)只應(yīng)聲明一次。否則,如果讀者沒有意識(shí)到此后的聲明,這將會(huì)造成混淆。

            函數(shù)聲明中盡可能使用 const

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

            示例
            const T f(...); //函數(shù)返回一個(gè)常量
            		         //對(duì)象。
            T f(T* const arg);	        //函數(shù)接受一個(gè)常量指針
            			          //為其參數(shù)。
            //可以改變指針?biāo)傅膶?duì)象,
            //但不可以改變指針本身。
            T f(const T* arg);      //函數(shù)參數(shù)為指針類型
            T f(const T& arg);      // 函數(shù)參數(shù)為引用類型
                                    //它們都指向一個(gè)常量對(duì)象。指針可以
                                    //改變,但所指的對(duì)象
                                    //不能改變。
            T f(const T* const arg);  //函數(shù)參數(shù)為
                                      //常量指針,它指向常量對(duì)象。
                                      //指針和它所指的對(duì)象
                                      //都不改變。
            T f(...) const;  //函數(shù)沒有副作用:
                             //不改變它對(duì)象的狀態(tài)
                             //所以可應(yīng)用于
                             //常量對(duì)象。

            避免利用值傳遞對(duì)象

            利用值傳遞和返回對(duì)象可能帶來構(gòu)造函數(shù)和析構(gòu)函數(shù)的大量開支。可通過引用傳遞和返回對(duì)象,這樣可避免構(gòu)造函數(shù)和析構(gòu)函數(shù)的開支。
            可用 Const 引用指定通過引用傳遞的實(shí)參是不可改變的。復(fù)制構(gòu)造函數(shù)和賦值操作符是典型的示例:

            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);

            調(diào)用函數(shù) return_by_value,其實(shí)參為 an_object,此時(shí)調(diào)用 the_class 的復(fù)制構(gòu)造函數(shù)將 an_object 復(fù)制到 a_copy。再次調(diào)用 the_class 的復(fù)制構(gòu)造函數(shù)將 a_copy 復(fù)制到函數(shù)返回的臨時(shí)對(duì)象中。從函數(shù)返回時(shí)調(diào)用 the_class 的析構(gòu)函數(shù)銷毀 a_copy。一段時(shí)間之后再次調(diào)用 the_class 的析構(gòu)函數(shù)銷毀 return_by_value 返回的對(duì)象。以上不完成任何事的函數(shù)的所有開支是兩個(gè)構(gòu)造函數(shù)和兩個(gè)析構(gòu)函數(shù)。
            如果 the_class 是個(gè)派生類且包含其他類的成員數(shù)據(jù),情況會(huì)更糟:會(huì)調(diào)用基類以及所包含類的構(gòu)造函數(shù)和析構(gòu)函數(shù),因此函數(shù)調(diào)用所帶來的構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用就會(huì)隨之急劇增長(zhǎng)。

            注意:

            以上指南似乎旨在建議開發(fā)人員在傳遞和返回對(duì)象時(shí)總是使用引用類型,但應(yīng)格外當(dāng)心不要返回引用到局部對(duì)象上,當(dāng)需要對(duì)象時(shí),也不要返回引用。返回引用到局部對(duì)象會(huì)造成災(zāi)難性后果,因?yàn)楹瘮?shù)返回時(shí),所返回的引用綁定到了所銷毀的對(duì)象上。

            不能返回引用到局部對(duì)象

            離開函數(shù)作用域時(shí)會(huì)銷毀局部對(duì)象;使用銷毀了的對(duì)象會(huì)造成災(zāi)難。

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

            違背這一原則將導(dǎo)致內(nèi)存泄漏

            示例
            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;

            因?yàn)樵谟?jì)算加和 sum 值時(shí)沒有存儲(chǔ)操作符“+”的中間結(jié)果,因此中間對(duì)象不能刪除,否則會(huì)導(dǎo)致內(nèi)存泄漏。

            不要返回非常量引用或指針到成員數(shù)據(jù)上

            違背了這一原則也就違背了數(shù)據(jù)的封裝性,可能導(dǎo)致不好的結(jié)果。

            宏擴(kuò)展使用內(nèi)嵌定義函數(shù)不使用 #define

            但要有選擇的使用內(nèi)嵌定義:只對(duì)非常小的函數(shù)才使用;內(nèi)嵌定義大型函數(shù)可能導(dǎo)致代碼膨脹。
            由于用戶代碼編譯時(shí)需要內(nèi)嵌定義函數(shù)的實(shí)現(xiàn),因此內(nèi)嵌定義函數(shù)也增加了模塊間編譯的依賴。
            [Meyers, 1992] 提供了以下不良宏使用極端示例的詳細(xì)討論:

            示例

            不要這樣做:

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

            而應(yīng)這樣做:

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

            MAX 有好幾個(gè)問題:它不是安全的類型;它的行為不是確定的:

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

            使用默認(rèn)參數(shù)而不使用函數(shù)重載

            當(dāng)只使用一個(gè)算法并且此算法可由少量幾個(gè)參數(shù)進(jìn)行參數(shù)化時(shí),使用默認(rèn)參數(shù)而不使用函數(shù)重載。
            使用默認(rèn)參數(shù)有利于減少重載函數(shù)的數(shù)量、提高可維護(hù)性、減少函數(shù)調(diào)用時(shí)所需的實(shí)參數(shù)量、以及提高代碼的可讀性。

            使用函數(shù)重載表達(dá)常用語義

            當(dāng)同一語義操作需要多個(gè)實(shí)現(xiàn)時(shí)使用函數(shù)重載,但重載使用不同的實(shí)參類型。
            重載操作符時(shí)保留其傳統(tǒng)含義。不要忘記定義關(guān)系操作符,如操作符 operator==operator!=

            避免重載以指針和整形數(shù)為實(shí)參的函數(shù)

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

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

            以下調(diào)用可能會(huì)導(dǎo)致混淆:

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

            此重載解析為 f(int) 而不是 f(char*)

            令操作符 operator= 返回對(duì) *this 的引用

            C++ 允許賦值鏈:

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

            因?yàn)閺?fù)制操作符是右結(jié)合的,將字符串“A string”賦給 z,再將 z 賦給 y,最后將 y 賦給 x。以從右到左的順序,對(duì)每個(gè)在 = 右端的表達(dá)式有效的調(diào)用一次 operator= 。這也意味著每一次 operator= 運(yùn)算的結(jié)果都是一個(gè)對(duì)象,但其左端或右端的對(duì)象都有可能成為返回值。
            使用復(fù)制操作符好的做法總有以下形式:

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

            只有左端的對(duì)象是可能的(rhs 為常量引用,lhs 為變量引用),因此應(yīng)返回 *this。詳情請(qǐng)參見[Meyers, 1992]

            使 operator= 檢查自賦值

            執(zhí)行檢查有兩點(diǎn)重要的理由:首先,派生類對(duì)象的賦值涉及到調(diào)用每一個(gè)基類(在繼承層次結(jié)構(gòu)中位于此類的上方)的賦值操作符,跳過這些操作符就可以節(jié)省很多運(yùn)行時(shí)間。其次,在復(fù)制“rvalue”對(duì)象前,賦值涉及到解構(gòu)“l(fā)value”對(duì)象。在自賦值時(shí),rvalue 對(duì)象在賦值前就已銷毀了,因此賦值的結(jié)果是不確定的。

            盡可能減少?gòu)?fù)雜性

            不要書寫過長(zhǎng)的函數(shù),例如其代碼超過 60 行。
            盡可能減少返回語句的數(shù)量,1 是理想值。
            盡量使循環(huán)復(fù)雜性降到 10 以下(對(duì)單一退出語句函數(shù)為判定語句總數(shù)加 1)。
            盡量使擴(kuò)展循環(huán)復(fù)雜性降到 15 以下(對(duì)單一退出語句函數(shù)為判定語句+邏輯操作符+1的和)。
            盡量減小引用的平均最大作用范圍(局部對(duì)象聲明與對(duì)象第一次使用間的行距)。

            類型

            定義項(xiàng)目范圍的全局系統(tǒng)類型

            大型項(xiàng)目中通常有整個(gè)系統(tǒng)中經(jīng)常使用的類型的集合;這種情況下,將這些類型收集入一個(gè)或多個(gè)低級(jí)全局實(shí)用程序的名字空間中,這樣做是明智的(請(qǐng)參見“避免使用基本類型”的示例)。

            避免使用基本類型

            當(dāng)對(duì)可移植性要求較高、需要控制數(shù)字對(duì)象占據(jù)的內(nèi)存空間或需要一個(gè)具體范圍的值時(shí),不使用基本類型。這種情況下最好通過使用適當(dāng)?shù)幕绢愋兔鞔_聲明帶有大小限制的類型的名稱。
            確保基本類型不會(huì)通過循環(huán)計(jì)數(shù)器、數(shù)組下標(biāo)等潛回代碼中。

            示例
            namespace system_types {
            typedef unsigned char byte;
            typedef short int integer16; //16 位有符號(hào)整數(shù)
            typedef int integer32; //32 位有符號(hào)整數(shù)
            typedef unsigned short int natural16; //16 位無符號(hào)整數(shù)
            typedef unsigned int natural32; //32 位無符號(hào)整數(shù)
            ...
            }      
            基本原理

            基本類型的表示依賴于具體實(shí)施。

            使用 typedef 創(chuàng)建同義詞來加強(qiáng)局部含義

            對(duì)現(xiàn)有名稱使用 typedef 創(chuàng)建同義詞,提供更有意義的局部名稱并提高易讀性(這樣做不會(huì)增加運(yùn)行時(shí)間)。
            typedef 也可用來提供受限名稱的速記法。

            示例
            //標(biāo)準(zhǔn)庫(kù)的向量聲明
            //
            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 創(chuàng)建的名稱時(shí),同一代碼段中不要混合使用原名稱和它的同義詞。

            常量與對(duì)象

            避免使用常量值

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

            定義常量時(shí)避免使用 #define 預(yù)處理指示符

            代之以 constenum
            不要這樣做:

            #define LIGHT_SPEED 3E8
            而應(yīng)這樣做:
            const int light_speed = 3E8;
            或調(diào)整數(shù)組大小如下:
            enum { small_buffer_size = 100, 
            large_buffer_size = 1000 };
            基本原理

            因?yàn)橛?code> #defines 引入的名稱在編譯預(yù)處理時(shí)會(huì)被替換掉,不出現(xiàn)在符號(hào)表中,因此調(diào)試就更加困難了。

            對(duì)象聲明靠近其第一次使用點(diǎn)
            聲明時(shí)總要初始化 const 對(duì)象

            未聲明為 externconst 對(duì)象有內(nèi)部鏈接,聲明時(shí)初始化這些常量對(duì)象允許在編譯時(shí)使用初始化函數(shù)。

            永遠(yuǎn)不要忘記常量對(duì)象的“不變性”

            常量對(duì)象可能存在于只讀存儲(chǔ)器中。

            定義時(shí)初始化對(duì)象

            如果對(duì)象不是自初始化的,在對(duì)象定義中指定初始值。如果不可能指定一個(gè)有意義的初始值,則賦值“nil”或考慮后面再作定義。
            對(duì)大型對(duì)象,通常不建議先構(gòu)造對(duì)象,此后再使用賦值語句初始化的作法。因?yàn)檫@樣做的代價(jià)高昂(請(qǐng)參見“使用初始化構(gòu)造函數(shù)而不使用構(gòu)造函數(shù)中的賦值語句”)。
            如果構(gòu)造時(shí)不可能合適的初始化對(duì)象,則使用傳統(tǒng)的“nil”值進(jìn)行對(duì)象初始化,它意味著“未初始化”。只在初始化過程中聲明“不可用但值已知”的對(duì)象時(shí)才使用 nil 值。但算法在受控模式下可以拒絕接受:在對(duì)象合適的初始化之前使用它時(shí)可以指出這是未經(jīng)初始化的變量錯(cuò)誤。
            注意有時(shí)不可能所有類型都聲明為 nil 值,尤其是在模運(yùn)算類型中,例如角度。I這種情況下選擇最不可能出現(xiàn)的值。


            第六章

            表達(dá)式和語句

            本章對(duì)不同種類的 C++ 表達(dá)式和語句提供指南。

            表達(dá)式

            使用冗余的圓括號(hào)使復(fù)合表達(dá)式含義更加清晰
            避免表達(dá)式的過深嵌套

            表達(dá)式的嵌套層數(shù)定義為:忽略操作符優(yōu)先級(jí)原則時(shí),從左到右求表達(dá)式的值所需圓括號(hào)對(duì)的嵌套數(shù)。
            嵌套層數(shù)過多會(huì)使表達(dá)式難以理解。

            不要假定任何特殊的表達(dá)式計(jì)算次序

            除非計(jì)算次序由操作符指定(如逗號(hào)操作符、三元表達(dá)式、以及連接和分離),否則不要假定任何特殊的計(jì)算次序,因?yàn)檫@種假定可能導(dǎo)致嚴(yán)重的混淆并降低可移植性。
            例如,同一語句中不要混合使用變量的增 1 和減 1 操作。

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

            空指針使用 0 而不使用 NULL

            空指針應(yīng)使用 0 還是 NULL 是一個(gè)有高度爭(zhēng)議的論題。
            C 和 C++ 中定義任何零值常量表達(dá)式都可解釋為空指針。因?yàn)榱汶y以閱讀,并且不贊成使用常量,傳統(tǒng)上程序員使用宏 NULL 作為空指針。不幸的是,NULL 沒有可移植的定義。一些 ANSI C 編譯器使用 (void *)0,但對(duì) C++ 來說這不是一個(gè)好的選擇:

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

            因此任何形如 (T*)0 而非一個(gè) 0 的 NULL 定義,在C++ 中都需要類型轉(zhuǎn)換。歷史上,很多指南都擁護(hù)使用 0 代替空指針以減少類型轉(zhuǎn)換、增強(qiáng)代碼的可移植性。但許多 C++ 開發(fā)人員還是感覺使用 NULL 比 0 更加舒服,也爭(zhēng)辯說當(dāng)今多數(shù)的編譯器(準(zhǔn)確的說是多數(shù)頭文件)將 NULL 實(shí)施為 0。
            本指南傾向使用 0,因?yàn)椴还?NULL 的值是多少,0 都是有效的。但由于爭(zhēng)議,這一點(diǎn)降級(jí)為一個(gè)小技巧,可在適當(dāng)?shù)臅r(shí)候遵循或忽略它。

            不要使用舊類型轉(zhuǎn)換

            使用新類型轉(zhuǎn)換符(dynamic_cast、static_cast、reinterpret_cast、const_cast)而不要使用舊類型轉(zhuǎn)換符。
            如果您沒有新類型轉(zhuǎn)換符,避免同時(shí)轉(zhuǎn)換,特別是向下轉(zhuǎn)換(將基類對(duì)象轉(zhuǎn)換成派生類對(duì)象)。
            使用轉(zhuǎn)換操作符如下:

            • dynamic_cast - 類同一層次(子類型)成員間的的轉(zhuǎn)換,使用運(yùn)行類型信息(對(duì)帶虛函數(shù)的類,運(yùn)行類型信息是可用的)。這種類間的轉(zhuǎn)換一定是安全的。
            • static_cast - 類同一層次成員間的的轉(zhuǎn)換,不使用運(yùn)行類型信息;所以這可能是不安全的。如果程序員不能保證類型的安全性,則使用 dynamic_cast。
            • reinterpret_cast - 不相關(guān)指針類型和整形(整數(shù))間的轉(zhuǎn)換;這是不安全的,只應(yīng)在所提及的類型間使用。
            • const_cast - 將指定為 const 型函數(shù)實(shí)參的“不變性”轉(zhuǎn)換掉。注意:const_cast 不打算將確實(shí)定義為常量對(duì)象(它可能位于只讀存儲(chǔ)器中)的“不變性”轉(zhuǎn)換掉。

            不要使用 typeid 實(shí)現(xiàn)類型轉(zhuǎn)換邏輯:使類型轉(zhuǎn)換操作符完成類型檢查和轉(zhuǎn)換的操作不可再分,詳情請(qǐng)參見 [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 () {
            
            }
            }      
            基本原理

            舊類型轉(zhuǎn)換使類型系統(tǒng)失效,可能導(dǎo)致編譯器難以察覺的缺陷:內(nèi)存管理系統(tǒng)可能會(huì)崩潰,虛函數(shù)表可能遭嚴(yán)重錯(cuò)誤修改,當(dāng)對(duì)象作為派生類對(duì)象訪問時(shí)無關(guān)對(duì)象可能會(huì)遭破壞。注意即使讀訪問也可能造成破壞,因?yàn)橛锌赡芤昧瞬⒉淮嬖诘闹羔樆騾^(qū)域。
            新類型轉(zhuǎn)換操作符使類型轉(zhuǎn)換更加安全(多數(shù)情況下)、更加明確。

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

            不要使用舊的 Boolean 宏或常數(shù):沒有標(biāo)準(zhǔn)的布爾值 true(真);代之以新的 bool 類型。

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

            因?yàn)閭鹘y(tǒng)上 true 值 (1 或 !0) 沒有標(biāo)準(zhǔn)值,非零表達(dá)式和真值的比較就可能無效。
            代之以使用布爾表達(dá)式。

            示例

            不要這樣做:

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

            指針不要與不在同一數(shù)組中的對(duì)象比較

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

            已刪除的對(duì)象指針要賦予空指針值

            設(shè)置已刪除對(duì)象的指針為空指針可避免災(zāi)難的發(fā)生:重復(fù)刪除非空指針是有害的,但重復(fù)刪除空指針是無害的。
            即使在函數(shù)返回前,刪除操作后也總要賦一個(gè)空指針,因?yàn)橐院罂赡芴砑有碌拇a。

            語句

            當(dāng)從布爾表達(dá)式分支時(shí)使用 if 語句
            當(dāng)從離散值分支時(shí)使用 switch 語句

            當(dāng)分支條件為離散值時(shí)使用 switch 語句而不使用一系列的“else if”。

            一定為 switch 語句提供一個(gè) default 分支以記錄錯(cuò)誤

            switch 語句應(yīng)總包含一個(gè) default 分支,default 分支應(yīng)用以捕獲錯(cuò)誤。
            這一原則保證了當(dāng)引入新的 switch 值而處理這一新值的分支被刪除時(shí),現(xiàn)有的 default 分支可以捕獲到錯(cuò)誤。

            當(dāng)循環(huán)需要迭代前測(cè)試時(shí)使用 for 語句或 while 語句

            當(dāng)?shù)脱h(huán)的結(jié)束是根據(jù)循環(huán)計(jì)數(shù)器的值時(shí)使用 for 語句不使用 while 語句。

            當(dāng)循環(huán)需要迭代后測(cè)試時(shí)使用 do while 語句
            循環(huán)中避免使用 jump 語句

            避免使用除循環(huán)結(jié)束條件以外的循環(huán)退出方式(使用 break、returngoto),不要使用 continue 不成熟的跳轉(zhuǎn)到下一迭代上。這減少了控制路徑流程的數(shù)量,使代碼更易理解。

            不要使用 goto 語句

            這是一條通用原則。

            避免嵌套作用域內(nèi)隱藏標(biāo)識(shí)符

            這會(huì)使讀者模糊不清,維護(hù)時(shí)造成潛在的危險(xiǎn)。


            第七章

            特殊主題

            本章為內(nèi)存管理與錯(cuò)誤報(bào)告提供指南

            內(nèi)存管理

            C 和 C++ 內(nèi)存操作要避免混合使用

            C 庫(kù)函數(shù) malloccallocrealloc 不應(yīng)用于分配對(duì)象空間:此時(shí)應(yīng)使用 C++ 操作符 new。
            只有在內(nèi)存?zhèn)鬟f給C 庫(kù)函數(shù)處理時(shí),才使用 C 函數(shù)分配內(nèi)存。
            不要使用 delete 來釋放由 C 函數(shù)分配的內(nèi)存,或釋放由 new 創(chuàng)建的對(duì)象。

            刪除由 new 創(chuàng)建的數(shù)組對(duì)象時(shí)總使用 delete[]

            使用 delete 刪除數(shù)組對(duì)象時(shí)如果不使用空方括號(hào)(“[]”),則只刪除數(shù)組的第一個(gè)元素,因此導(dǎo)致內(nèi)存泄漏。

            錯(cuò)誤處理和異常

            由于使用 C++ 異常機(jī)制的經(jīng)驗(yàn)不足,此處的指南將來可能會(huì)作較大的改變。
            C++ 草擬標(biāo)準(zhǔn)定義了兩大類錯(cuò)誤:邏輯錯(cuò)誤和運(yùn)行錯(cuò)誤。邏輯錯(cuò)誤是可避免的編程錯(cuò)誤。運(yùn)行錯(cuò)誤定義為程序范圍外事件導(dǎo)致的錯(cuò)誤。
            使用異常的通用規(guī)則為:正常條件下的系統(tǒng)如果沒有重載或硬件錯(cuò)誤則不應(yīng)當(dāng)產(chǎn)生任何異常。

            開發(fā)過程中多使用聲明語句以發(fā)現(xiàn)錯(cuò)誤

            開發(fā)過程中使用函數(shù)的前置和后置條件聲明語句以發(fā)現(xiàn)“倒斃”錯(cuò)誤。
            在實(shí)現(xiàn)最終錯(cuò)誤處理代碼前,聲明語句提供了簡(jiǎn)單有用的臨時(shí)錯(cuò)誤監(jiān)測(cè)機(jī)制。聲明語句還有額外的好處,使用“NDEBUG”預(yù)處理符,可在編譯時(shí)去掉這些聲明(請(qǐng)參見“用具體值定義 NDEBUG 預(yù)處理符”)。
            傳統(tǒng)上使用宏 assert 達(dá)到這一目的;但文獻(xiàn) [Stroustrup, 1994] 提供了一個(gè)可替代的模板如下。

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

            只在真實(shí)的異常情況時(shí)使用異常

            對(duì)經(jīng)常的、預(yù)料中的事件不要使用異常:異常打斷了代碼的正常控制流程,使它難以理解和維護(hù)。
            預(yù)料中的事件應(yīng)用代碼的正常控制流程處理;需要時(shí)使用函數(shù)返回值或“out”參數(shù)狀態(tài)碼。
            異常也不應(yīng)用于實(shí)現(xiàn)控制結(jié)構(gòu):這會(huì)是另一種形式的“goto”語句。

            從標(biāo)準(zhǔn)異常中派生項(xiàng)目異常

            這保證了所有異常都支持常用操作的最小集合并可由一小組高級(jí)處理程序處理。
            使用邏輯錯(cuò)誤(域錯(cuò)誤、非法實(shí)參錯(cuò)誤、長(zhǎng)度錯(cuò)誤和越界錯(cuò)誤)表示應(yīng)用程序域錯(cuò)誤、函數(shù)調(diào)用傳遞了非法實(shí)參、對(duì)象構(gòu)造超出了它們?cè)试S的大小以及實(shí)參值不在允許范圍內(nèi)。
            使用運(yùn)行錯(cuò)誤(范圍錯(cuò)誤和溢出錯(cuò)誤)表示只有在運(yùn)行時(shí)才能查出的算術(shù)和配置錯(cuò)誤、數(shù)據(jù)已遭破壞或資源耗盡錯(cuò)誤。

            給定抽象盡量少使用異常

            大型系統(tǒng)中,每一層都不得不處理大量的異常會(huì)使代碼難以閱讀和維護(hù)。異常處理可能會(huì)嚴(yán)重影響了正常處理。
            減少使用異常的方法有:
            通過使用少量的異常種類在抽象間共享異常。
            引發(fā)派生于標(biāo)準(zhǔn)異常的特殊異常,處理更通用的異常。
            為對(duì)象添加“異常”狀態(tài),提供明確檢查對(duì)象合法性的原語。

            將所有異常聲明為已引發(fā)

            產(chǎn)生異常(而不僅僅是傳遞異常)的函數(shù)應(yīng)在它們的異常規(guī)約中將所有異常聲明為已引發(fā):它們不應(yīng)平靜的產(chǎn)生異常而不警告它們的用戶。

            在異常首次出現(xiàn)時(shí)就報(bào)告它

            開發(fā)過程中,采用合適的記錄機(jī)制盡早報(bào)告異常,包括在“異常引發(fā)點(diǎn)”。

            按照從派生結(jié)構(gòu)最底端到最頂端的順序定義異常處理

            定義異常處理要按照從派生結(jié)構(gòu)最底端到最頂端(即從最終子類到最初父類)的順序,這樣可以避免編寫無法到達(dá)的處理過程;請(qǐng)參見下面的示例 how-not-to-it。因?yàn)槭且月暶鞯捻樞蚱ヅ涮幚磉^程的,所以這也保證了異常由最合適的處理過程捕獲。

            示例

            不要這樣做:

            class base { ...};
            class derived : public base { ...};
            ...
            try {
            ...
            throw derived(...);
            //
            //引發(fā)一個(gè)派生類異常
            }
            catch (base& a_base_failure)
            //
            //但基類異常處理過程“捕獲”了它,因?yàn)?
            //基類處理過程首先匹配!
            {
            ...
            }
            catch (derived& a_derived_failure) 
            //
            //這一處理過程是無法達(dá)到的!
            { 
            ...
            }      

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

            避免使用捕獲所有異常的處理過程(處理過程聲明使用 “…”),除非重引發(fā)了異常只有在做本地內(nèi)務(wù)管理時(shí)才使用捕獲所有異常的處理過程,這時(shí)應(yīng)重引發(fā)異常以避免掩蓋了本層不能處理此異常的事實(shí)。

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

            確保函數(shù)狀態(tài)代碼有合適的值

            當(dāng)狀態(tài)代碼作為函數(shù)參數(shù)返回時(shí),要賦值給參數(shù)作為程序體中的第一個(gè)可執(zhí)行語句。系統(tǒng)的設(shè)置所有狀態(tài)的默認(rèn)值為成功或失敗。考慮函數(shù)所有可能的出口,包括異常處理。

            本地執(zhí)行安全檢查;不要希望您的客戶會(huì)這樣做

            如果沒有合適的輸入,函數(shù)就會(huì)產(chǎn)生錯(cuò)誤輸出,在函數(shù)中裝載代碼以受控方式監(jiān)測(cè)和報(bào)告非法輸入。不要依賴于標(biāo)注來告知客戶傳遞合適的值。事實(shí)上標(biāo)注遲早會(huì)被忽略掉,如果檢測(cè)到非法參數(shù)就會(huì)導(dǎo)致難以調(diào)試的錯(cuò)誤。


            第八章

            可移植性

            本章論述先驗(yàn)不可移植的語言特征。

            路徑名

            不要使用絕對(duì)代碼文件路徑名

            各操作系統(tǒng)中的路徑名不是以標(biāo)準(zhǔn)形式表示的。使用它們將會(huì)引入對(duì)平臺(tái)的依賴。

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

            數(shù)據(jù)表示

            類型的表示和排列高度依賴于機(jī)器的結(jié)構(gòu)。對(duì)表示和排列的假設(shè)會(huì)導(dǎo)致混淆,降低可移植性。

            不要假設(shè)類型的表示

            特別的,不可以在 int、長(zhǎng)整形或任何其他數(shù)字類型中存儲(chǔ)指針類型,因?yàn)檫@是高度不可移植的。

            不要假設(shè)類型的排列

            不要依賴于一個(gè)特殊的下溢或上溢行為
            盡可能使用“可伸縮”常量

            可伸縮常量避免了字長(zhǎng)變化的問題。

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

            類型轉(zhuǎn)換

            不要從一個(gè)“短”類型轉(zhuǎn)換成一個(gè)“長(zhǎng)類型”

            機(jī)器結(jié)構(gòu)可能指定了某種類型的排列方式。從需要較松散排列的類型轉(zhuǎn)換為需要較嚴(yán)密排列的類型可能會(huì)導(dǎo)致程序錯(cuò)誤。


            第九章

            復(fù)用

            本章為 C++ 代碼復(fù)用提供指南。

            盡可能使用標(biāo)準(zhǔn)庫(kù)構(gòu)件

            如果沒有標(biāo)準(zhǔn)庫(kù),則基于標(biāo)準(zhǔn)庫(kù)接口創(chuàng)建類,這有利于將來的移植。

            使用模板復(fù)用獨(dú)立于數(shù)據(jù)的行為

            當(dāng)行為不依賴于一個(gè)具體的數(shù)據(jù)類型時(shí),使用模板復(fù)用行為。

            使用公共繼承復(fù)用類接口(子類型化)

            使用 public 繼承表達(dá)“is a”關(guān)系并復(fù)用基類接口,還可有選擇的復(fù)用它們的實(shí)現(xiàn)。

            使用包容而不是私有繼承復(fù)用類的實(shí)現(xiàn)

            當(dāng)復(fù)用實(shí)現(xiàn)或建模“部分/整體”關(guān)系時(shí),避免使用私有繼承。復(fù)用未重新定義的實(shí)現(xiàn)最好由包容而非私有繼承來實(shí)現(xiàn)。
            當(dāng)需要重新定義基類的操作時(shí)使用私有繼承。

            謹(jǐn)慎使用多繼承

            多繼承應(yīng)謹(jǐn)慎使用,因?yàn)樗鼛砹嗽S多額外的復(fù)雜性。[Meyers, 1992] 提供了對(duì)潛在名稱歧義和重復(fù)繼承所帶來復(fù)雜性的詳細(xì)討論。復(fù)雜性來自于:
            歧義,當(dāng)多個(gè)類使用相同的名稱時(shí),任何對(duì)名稱不加限定的引用天生就是產(chǎn)生歧義的。可通過使用類名稱限定其成員名稱來解決歧義的問題。但這也帶來了不幸的后果:使多態(tài)無效且將虛函數(shù)準(zhǔn)變成了靜態(tài)綁定函數(shù)。
            從同一基類重復(fù)繼承(派生類通過繼承結(jié)構(gòu)的不同路徑多次繼承一個(gè)基類)多組數(shù)據(jù)成員產(chǎn)生了如下問題:應(yīng)當(dāng)使用多組數(shù)據(jù)成員中的哪一個(gè)?
            可使用虛繼承(對(duì)虛基類的繼承)防止多繼承數(shù)據(jù)成員。那么為什么不總使用虛繼承呢?虛繼承有負(fù)面影響,會(huì)改變基礎(chǔ)對(duì)象表示并降低訪問的效率。
            要求所有的繼承都是虛擬的,同時(shí)也就強(qiáng)加了包羅一切的空間,帶來了時(shí)間上的低效,實(shí)現(xiàn)這樣的政策過于獨(dú)斷了。
            因此,為了決定是使用虛繼承還是非虛繼承,多繼承需要類的設(shè)計(jì)者對(duì)將來類的使用有敏銳的洞察力。


            第十章

            編譯問題

            本章為編譯問題指南

            盡量減少對(duì)編譯的依賴

            模塊規(guī)約中不要包含只是此模塊實(shí)施所需的其他頭文件。
            避免在只需要指針或引用可見時(shí)就為了能看到其他類而在規(guī)約中包含頭文件;代之以預(yù)先聲明。

            示例
            //模塊 A 規(guī)約,包含于文件“A.hh”中
            #include "B.hh" //當(dāng)只有實(shí)施需要時(shí)
                            //不要包含。
            #include "C.hh" //只有引用需要時(shí)不要包含;
                            //代之以預(yù)先聲明。
            class C;
            class A
            {
            C* a_c_by_reference; //有 a 的引用。
            };
            // “A.hh”結(jié)束
            注意:

            盡量減少對(duì)編譯的依賴是某些設(shè)計(jì)代碼模式或模式的基本原理,如不同的命名:Handle(句柄)或 Envelope(信包)[Meyers, 1992],或 Bridge(橋)[Gamma] 類。將類抽象的責(zé)任在兩個(gè)關(guān)聯(lián)類間分割,一個(gè)提供類接口,另一個(gè)提供類實(shí)現(xiàn);因?yàn)槿魏螌?shí)現(xiàn)(實(shí)現(xiàn)類)上的改變都不再需要客戶重新編譯,因此類與其客戶間的依賴關(guān)系就最小化了。

            示例
            //模塊 A 規(guī)約,包含于文件“A.hh”中
            class A_implementation;
            class A
            {
            A_implementation* the_implementation;
            };
            //“A.hh”結(jié)束

            這一做法也允許接口類和實(shí)現(xiàn)類特殊化為兩個(gè)單獨(dú)的類層次結(jié)構(gòu)。

            用具體值定義 NDEBUG

            通常使用符號(hào) NDEBUG 在編譯過程中去掉由宏 assert 實(shí)現(xiàn)的聲明代碼。傳統(tǒng)做法是當(dāng)需要消除聲明語句時(shí)定義這一符號(hào);但是,開發(fā)人員經(jīng)常沒有意識(shí)到聲明的存在,因此沒有定義符號(hào)。
            我們支持使用聲明的模板版本;這種情況下符號(hào) NDEBUG 必須明確的賦值:需要聲明代碼時(shí)為 0;要消除時(shí)為非 0。任何沒有提供符號(hào) NDEBUG 以具體值的聲明代碼最終編譯時(shí)將產(chǎn)生編譯錯(cuò)誤;因此,提醒了開發(fā)人員注意聲明代碼的存在。


            指南總結(jié)

            以下是本手冊(cè)所有指南的總結(jié)。

            要求或限制

            使用常識(shí)
            通常使用 #include 來訪問模塊的規(guī)約
            永遠(yuǎn)不要聲明以一個(gè)或多個(gè)下劃線 ('_') 開頭的名稱
            將全局聲明限定在名字空間中
            明確聲明構(gòu)造函數(shù)的類使用默認(rèn)構(gòu)造函數(shù)
            帶有指針類型數(shù)據(jù)成員的類要聲明其復(fù)制構(gòu)造函數(shù)和賦值操作符
            不要重復(fù)聲明構(gòu)造函數(shù)參數(shù)有默認(rèn)值
            將析構(gòu)函數(shù)總是聲明為 virtual 類型
            不要重定義非虛函數(shù)
            初始化構(gòu)造函數(shù)不要調(diào)用成員函數(shù)
            不能返回引用到局部對(duì)象
            不可返回由 new 初始化,之后又已解除引用的指針
            不要返回非常量引用或指針到成員數(shù)據(jù)上
            令操作符 operator= 返回對(duì) *this 的引用
            使 operator= 檢查自賦值
            永遠(yuǎn)不要忘記常量對(duì)象的“不變性”
            不要假定任何特殊的表達(dá)式計(jì)算次序
            不要使用舊類型轉(zhuǎn)換
            Boolean(布爾)表達(dá)式使用新的 bool 類型
            布爾值 true(真)之間不要直接比較
            指針不要與不在同一數(shù)組中的對(duì)象比較
            已刪除的對(duì)象指針要賦予空指針值
            一定為 switch 語句提供一個(gè) default 分支以記錄錯(cuò)誤
            不要使用 goto 語句
            C 和 C++ 內(nèi)存操作要避免混合使用
            刪除由 new 創(chuàng)建的數(shù)組對(duì)象時(shí)總使用 delete[]
            不要使用絕對(duì)代碼文件路徑名
            不要假設(shè)類型的表示
            不要假設(shè)類型的排列
            不要依賴于一個(gè)特殊的下溢或上溢行為
            不要從一個(gè)“短”類型轉(zhuǎn)換成一個(gè)“長(zhǎng)類型”
            用具體值定義 NDEBUG

            建議

            不同文件中放置模塊規(guī)約與實(shí)現(xiàn)
            只選擇一組文件擴(kuò)展名以區(qū)分頭文件和實(shí)施文件
            每個(gè)模塊規(guī)約避免定義多個(gè)類
            避免將私有實(shí)施聲明置于模塊的規(guī)約中
            將模塊內(nèi)嵌函數(shù)的定義置于單獨(dú)的文件中
            如果程序規(guī)模是個(gè)要求考慮的問題,就把大的模塊分割成多個(gè)變換單元
            分離平臺(tái)依賴性
            防止重復(fù)文件包含的保護(hù)

            對(duì)嵌套語句使用小的、一致的縮進(jìn)風(fēng)格
            相對(duì)于函數(shù)名或作用域名縮進(jìn)函數(shù)參數(shù)
            使用能夠適合標(biāo)準(zhǔn)打印紙大小的最大行寬
            使用一致?lián)Q行
            使用 C++ 風(fēng)格的注釋而非 C 風(fēng)格的注釋
            注釋與源代碼盡可能靠攏
            避免行末注釋
            避免注釋頭
            使用空注釋行分離注釋段
            避免冗余
            編寫自記錄代碼而非注釋
            記錄類與函數(shù)
            選擇一個(gè)名稱約定,使用它要前后一致
            避免使用只靠字母大小寫才能區(qū)分的名稱
            避免使用縮寫
            避免使用后綴來表明程序語言結(jié)構(gòu)
            選擇清晰的、易辨認(rèn)的、有意義的名稱
            使用名稱的正確拼寫
            布爾值使用正值謂詞從句
            使用名字空間由子系統(tǒng)或庫(kù)劃分潛在全局名稱
            類的名稱使用名詞或名詞短語
            過程類型的函數(shù)名稱使用動(dòng)詞
            當(dāng)需要使用同一通用含義時(shí),使用函數(shù)的重載
            實(shí)參名使用語法元素強(qiáng)調(diào)其含義
            異常名選用否定的含義
            異常名使用項(xiàng)目定義過的形容詞
            浮點(diǎn)指數(shù)和十六進(jìn)制數(shù)使用大寫字母
            使用名字空間劃分非類功能
            盡量不使用全局和名字空間范圍的數(shù)據(jù)
            使用 class 而不是 struct 來實(shí)現(xiàn)抽象數(shù)據(jù)類型
            以可訪問權(quán)限逐次降低的順序聲明類的成員
            抽象數(shù)據(jù)類型避免聲明公共或保護(hù)數(shù)據(jù)成員
            使用友元保留封裝性
            避免在類聲明中定義函數(shù)
            避免聲明太多的轉(zhuǎn)換操作符和單參數(shù)構(gòu)造函數(shù)
            謹(jǐn)慎使用非虛函數(shù)
            使用初始化構(gòu)造函數(shù)而不是使用構(gòu)造函數(shù)中的賦值語句
            注意構(gòu)造函數(shù)和析構(gòu)函數(shù)調(diào)用時(shí)的情況
            整形類常量使用 static const
            一定要明確聲明函數(shù)的返回值類型
            函數(shù)聲明中要提供正規(guī)參數(shù)名稱
            函數(shù)要盡量只有一個(gè)返回點(diǎn)
            避免創(chuàng)建對(duì)全局有副作用的函數(shù)
            以重要性和活躍性遞減的順序聲明函數(shù)的參數(shù)
            避免聲明帶有不定參數(shù)個(gè)數(shù)的函數(shù)
            避免重復(fù)聲明帶有默認(rèn)參數(shù)的函數(shù)
            函數(shù)聲明中盡可能使用 const
            避免利用值傳遞對(duì)象
            宏擴(kuò)展使用內(nèi)嵌定義函數(shù)不使用 #define
            使用默認(rèn)參數(shù)而不使用函數(shù)重載
            使用函數(shù)重載表達(dá)常用語義
            避免重載以指針和整形數(shù)為實(shí)參的函數(shù)
            盡可能減少?gòu)?fù)雜性
            避免使用基本類型
            避免使用常量值
            定義常量時(shí)避免使用 #define 預(yù)處理指示符
            對(duì)象聲明靠近其第一次使用點(diǎn)
            聲明時(shí)總要初始化 const 對(duì)象
            定義時(shí)初始化對(duì)象
            當(dāng)從布爾表達(dá)式分支時(shí)使用 if 語句
            當(dāng)從離散值分支時(shí)使用 switch 語句
            當(dāng)循環(huán)需要迭代前測(cè)試時(shí)使用 for 語句或 while 語句
            當(dāng)循環(huán)需要迭代后測(cè)試時(shí)使用 do while 語句
            循環(huán)中避免使用 jump 語句
            避免嵌套作用域內(nèi)隱藏標(biāo)識(shí)符
            開發(fā)過程中多使用聲明語句以發(fā)現(xiàn)錯(cuò)誤
            只在真實(shí)的異常情況時(shí)使用異常
            從標(biāo)準(zhǔn)異常中派生項(xiàng)目異常
            給定抽象盡量少使用異常
            將所有異常聲明為已引發(fā)
            按照從派生結(jié)構(gòu)最底端到最頂端的順序定義異常處理
            避免使用捕獲所有異常的處理過程
            確保函數(shù)狀態(tài)代碼有合適的值
            本地執(zhí)行安全檢查;不要希望您的客戶會(huì)這樣做
            盡可能使用“可伸縮”常量
            盡可能使用標(biāo)準(zhǔn)庫(kù)構(gòu)件

            提示

            定義項(xiàng)目范圍的全局系統(tǒng)類型
            使用 typedef 創(chuàng)建同義詞來加強(qiáng)局部含義
            使用冗余的圓括號(hào)使復(fù)合表達(dá)式含義更加清晰
            避免表達(dá)式的過深嵌套
            空指針使用 0 而不使用 NULL
            在異常首次出現(xiàn)時(shí)就報(bào)告它


            參考文獻(xiàn)

            [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++.


            ? 1987 - 2001 Rational Software Corporation。版權(quán)所有。

            分欄顯示 Rational Unified Process

            Rational Unified Process??

            posted on 2006-04-24 10:09 楊粼波 閱讀(819) 評(píng)論(0)  編輯 收藏 引用 所屬分類: Windows編程

            高清免费久久午夜精品| 亚洲午夜精品久久久久久app| 精品国产乱码久久久久久呢| 精品久久久中文字幕人妻| 国产精品女同久久久久电影院| 久久精品国产99国产精偷| 久久久精品波多野结衣| 久久综合香蕉国产蜜臀AV| 亚洲狠狠综合久久| 一本色道久久综合狠狠躁| 国产精久久一区二区三区| 久久强奷乱码老熟女网站| 国产福利电影一区二区三区久久老子无码午夜伦不| 久久国产精品偷99| 欧美黑人激情性久久| 久久99九九国产免费看小说| 狠狠色噜噜狠狠狠狠狠色综合久久| 欧美国产精品久久高清| 香蕉久久夜色精品国产小说| 久久久婷婷五月亚洲97号色| 久久热这里只有精品在线观看| 久久久久亚洲AV成人网人人网站| 国产精品无码久久综合| 国内精品久久久久影院优 | 97久久精品人妻人人搡人人玩| 久久99精品国产麻豆婷婷| 996久久国产精品线观看| 亚洲国产精品无码久久久不卡| 久久亚洲国产精品123区| 91精品国产综合久久四虎久久无码一级| 无码人妻久久久一区二区三区| 久久精品国产AV一区二区三区| 久久久久国产日韩精品网站| 久久精品国产亚洲Aⅴ蜜臀色欲| 午夜不卡888久久| Xx性欧美肥妇精品久久久久久 | 久久精品成人欧美大片| 久久99久久成人免费播放| 久久精品亚洲男人的天堂| 欧美粉嫩小泬久久久久久久 | 久久精品国产亚洲Aⅴ香蕉|