在线观看不卡亚洲电影_亚洲妓女99综合网_91青青青亚洲娱乐在线观看_日韩无码高清综合久久

鍍金池/ 教程/ C/ 其他 C++ 特性
結(jié)束語
作用域
頭文件
來自 Google 的奇技
注釋
規(guī)則特例
命名約定
其他 C++ 特性
格式

其他 C++ 特性

引用參數(shù)

Tip
    所以按引用傳遞的參數(shù)必須加上 ``const``.

定義: 在 C 語言中, 如果函數(shù)需要修改變量的值, 參數(shù)必須為指針, 如 int foo(int *pval). 在 C++ 中, 函數(shù)還可以聲明引用參數(shù): int foo(int &val).

優(yōu)點(diǎn): 定義引用參數(shù)防止出現(xiàn) (*pval)++ 這樣丑陋的代碼. 像拷貝構(gòu)造函數(shù)這樣的應(yīng)用也是必需的. 而且更明確, 不接受 NULL 指針.

缺點(diǎn): 容易引起誤解, 因?yàn)橐迷谡Z法上是值變量卻擁有指針的語義.

結(jié)論: 函數(shù)參數(shù)列表中, 所有引用參數(shù)都必須是 const:

void Foo(const string &in, string *out);

事實(shí)上這在 Google Code 是一個(gè)硬性約定: 輸入?yún)?shù)是值參或 const 引用, 輸出參數(shù)為指針. 輸入?yún)?shù)可以是 const 指針, 但決不能是 非 const 的引用參數(shù).

在以下情況你可以把輸入?yún)?shù)定義為 const 指針: 你想強(qiáng)調(diào)參數(shù)不是拷貝而來的, 在對(duì)象生存周期內(nèi)必須一直存在; 最好同時(shí)在注釋中詳細(xì)說明一下. bind2ndmem_fun 等 STL 適配器不接受引用參數(shù), 這種情況下你也必須把函數(shù)參數(shù)聲明成指針類型.

函數(shù)重載

Tip
    僅在輸入?yún)?shù)類型不同, 功能相同時(shí)使用重載函數(shù) (含構(gòu)造函數(shù)). 不要用函數(shù)重載模擬 `缺省函數(shù)參數(shù) <http://code.google.com/p/google-gflags/>`_.

定義: 你可以編寫一個(gè)參數(shù)類型為 const string& 的函數(shù), 然后用另一個(gè)參數(shù)類型為 const char* 的函數(shù)重載它:

            class MyClass {
                public:
                void Analyze(const string &text);
                void Analyze(const char *text, size_t textlen);
            };

優(yōu)點(diǎn): 通過重載參數(shù)不同的同名函數(shù), 令代碼更加直觀. 模板化代碼需要重載, 同時(shí)為使用者帶來便利.

缺點(diǎn): 限制使用重載的一個(gè)原因是在某個(gè)特定調(diào)用點(diǎn)很難確定到底調(diào)用的是哪個(gè)函數(shù). 另一個(gè)原因是當(dāng)派生類只重載了某個(gè)函數(shù)的部分變體, 會(huì)令很多人對(duì)繼承的語義產(chǎn)生困惑. 此外在閱讀庫的用戶代碼時(shí), 可能會(huì)因反對(duì)使用 缺省函數(shù)參數(shù) <http://code.google.com/p/google-gflags/>_ 造成不必要的費(fèi)解.

結(jié)論: 如果你想重載一個(gè)函數(shù), 考慮讓函數(shù)名包含參數(shù)信息, 例如, 使用 AppendString(), AppendInt() 而不是 Append().

缺省參數(shù)

Tip
    我們不允許使用缺省函數(shù)參數(shù).

優(yōu)點(diǎn): 多數(shù)情況下, 你寫的函數(shù)可能會(huì)用到很多的缺省值, 但偶爾你也會(huì)修改這些缺省值. 無須為了這些偶爾情況定義很多的函數(shù), 用缺省參數(shù)就能很輕松的做到這點(diǎn).

缺點(diǎn): 大家通常都是通過查看別人的代碼來推斷如何使用 API. 用了缺省參數(shù)的代碼更難維護(hù), 從老代碼復(fù)制粘貼而來的新代碼可能只包含部分參數(shù). 當(dāng)缺省參數(shù)不適用于新代碼時(shí)可能會(huì)導(dǎo)致重大問題.

結(jié)論: 我們規(guī)定所有參數(shù)必須明確指定, 迫使程序員理解 API 和各參數(shù)值的意義, 避免默默使用他們可能都還沒意識(shí)到的缺省參數(shù).

變長數(shù)組和 alloca()

Tip
    我們不允許使用變長數(shù)組和 ``alloca()``.

優(yōu)點(diǎn): 變長數(shù)組具有渾然天成的語法. 變長數(shù)組和 alloca() 也都很高效.

缺點(diǎn): 變長數(shù)組和 alloca() 不是標(biāo)準(zhǔn) C++ 的組成部分. 更重要的是, 它們根據(jù)數(shù)據(jù)大小動(dòng)態(tài)分配堆棧內(nèi)存, 會(huì)引起難以發(fā)現(xiàn)的內(nèi)存越界 bugs: "在我的機(jī)器上運(yùn)行的好好的, 發(fā)布后卻莫名其妙的掛掉了".

結(jié)論: 使用安全的內(nèi)存分配器, 如 scoped_ptr / scoped_array.

友元

Tip
    我們允許合理的使用友元類及友元函數(shù).

通常友元應(yīng)該定義在同一文件內(nèi), 避免代碼讀者跑到其它文件查找使用該私有成員的類. 經(jīng)常用到友元的一個(gè)地方是將 FooBuilder 聲明為 Foo 的友元, 以便 FooBuilder 正確構(gòu)造 Foo 的內(nèi)部狀態(tài), 而無需將該狀態(tài)暴露出來. 某些情況下, 將一個(gè)單元測試類聲明成待測類的友元會(huì)很方便.

友元擴(kuò)大了 (但沒有打破) 類的封裝邊界. 某些情況下, 相對(duì)于將類成員聲明為 public, 使用友元是更好的選擇, 尤其是如果你只允許另一個(gè)類訪問該類的私有成員時(shí). 當(dāng)然, 大多數(shù)類都只應(yīng)該通過其提供的公有成員進(jìn)行互操作.

異常

Tip
    我們不使用 C++ 異常.

優(yōu)點(diǎn):

  • 異常允許上層應(yīng)用決定如何處理在底層嵌套函數(shù)中 "不可能出現(xiàn)的" 失敗, 不像錯(cuò)誤碼記錄那么含糊又易出錯(cuò);

  • 很多現(xiàn)代語言都使用異常. 引入異常使得 C++ 與 Python, Java 以及其它 C++ 相近的語言更加兼容.

  • 許多第三方 C++ 庫使用異常, 禁用異常將導(dǎo)致很難集成這些庫.

  • 異常是處理構(gòu)造函數(shù)失敗的唯一方法. 雖然可以通過工廠函數(shù)或 Init() 方法替代異常, 但他們分別需要堆分配或新的 "無效" 狀態(tài);

  • 在測試框架中使用異常確實(shí)很方便.

缺點(diǎn):

  • 在現(xiàn)有函數(shù)中添加 throw 語句時(shí), 你必須檢查所有調(diào)用點(diǎn). 所有調(diào)用點(diǎn)得至少有基本的異常安全保護(hù), 否則永遠(yuǎn)捕獲不到異常, 只好 "開心的" 接受程序終止的結(jié)果. 例如, 如果 f() 調(diào)用了 g(), g() 又調(diào)用了 h(), h 拋出的異常被 f 捕獲, g 要當(dāng)心了, 很可能會(huì)因疏忽而未被妥善清理.
  • 更普遍的情況是, 如果使用異常, 光憑查看代碼是很難評(píng)估程序的控制流: 函數(shù)返回點(diǎn)可能在你意料之外. 這回導(dǎo)致代碼管理和調(diào)試?yán)щy. 你可以通過規(guī)定何時(shí)何地如何使用異常來降低開銷, 但是讓開發(fā)人員必須掌握并理解這些規(guī)定帶來的代價(jià)更大.

  • 異常安全要求同時(shí)采用 RAII 和不同編程實(shí)踐. 要想輕松編寫正確的異常安全代碼, 需要大量的支撐機(jī)制配合. 另外, 要避免代碼讀者去理解整個(gè)調(diào)用結(jié)構(gòu)圖, 異常安全代碼必須把寫持久化狀態(tài)的邏輯部分隔離到 "提交" 階段. 它在帶來好處的同時(shí), 還有成本 (也許你不得不為了隔離 "提交" 而整出令人費(fèi)解的代碼). 允許使用異常會(huì)驅(qū)使我們不斷為此付出代價(jià), 即使我們覺得這很不劃算.

  • 啟用異常使生成的二進(jìn)制文件體積變大, 延長了編譯時(shí)間 (或許影響不大), 還可能增加地址空間壓力;

  • 異常的實(shí)用性可能會(huì)慫恿開發(fā)人員在不恰當(dāng)?shù)臅r(shí)候拋出異常, 或者在不安全的地方從異常中恢復(fù). 例如, 處理非法用戶輸入時(shí)就不應(yīng)該拋出異常. 如果我們要完全列出這些約束, 這份風(fēng)格指南會(huì)長出很多!

結(jié)論: 從表面上看, 使用異常利大于弊, 尤其是在新項(xiàng)目中. 但是對(duì)于現(xiàn)有代碼, 引入異常會(huì)牽連到所有相關(guān)代碼. 如果新項(xiàng)目允許異常向外擴(kuò)散, 在跟以前未使用異常的代碼整合時(shí)也將是個(gè)麻煩. 因?yàn)?Google 現(xiàn)有的大多數(shù) C++ 代碼都沒有異常處理, 引入帶有異常處理的新代碼相當(dāng)困難.

鑒于 Google 現(xiàn)有代碼不接受異常, 在現(xiàn)有代碼中使用異常比在新項(xiàng)目中使用的代價(jià)多少要大一些. 遷移過程比較慢, 也容易出錯(cuò). 我們不相信異常的使用有效替代方案, 如錯(cuò)誤代碼, 斷言等會(huì)造成嚴(yán)重負(fù)擔(dān).

我們并不是基于哲學(xué)或道德層面反對(duì)使用異常, 而是在實(shí)踐的基礎(chǔ)上. 我們希望在 Google 使用我們自己的開源項(xiàng)目, 但項(xiàng)目中使用異常會(huì)為此帶來不便, 因此我們也建議不要在 Google 的開源項(xiàng)目中使用異常. 如果我們需要把這些項(xiàng)目推倒重來顯然不太現(xiàn)實(shí).

對(duì)于 Windows 代碼來說, 有個(gè) :ref:特例 <windows-code>.

(YuleFox 注: 對(duì)于異常處理, 顯然不是短短幾句話能夠說清楚的, 以構(gòu)造函數(shù)為例, 很多 C++ 書籍上都提到當(dāng)構(gòu)造失敗時(shí)只有異??梢蕴幚? Google 禁止使用異常這一點(diǎn), 僅僅是為了自身的方便, 說大了, 無非是基于軟件管理成本上, 實(shí)際使用中還是自己決定)

運(yùn)行時(shí)類型識(shí)別

Tip
    我們禁止使用 RTTI.

定義: RTTI 允許程序員在運(yùn)行時(shí)識(shí)別 C++ 類對(duì)象的類型.

優(yōu)點(diǎn): RTTI 在某些單元測試中非常有用. 比如進(jìn)行工廠類測試時(shí), 用來驗(yàn)證一個(gè)新建對(duì)象是否為期望的動(dòng)態(tài)類型.

除測試外, 極少用到.

缺點(diǎn): 在運(yùn)行時(shí)判斷類型通常意味著設(shè)計(jì)問題. 如果你需要在運(yùn)行期間確定一個(gè)對(duì)象的類型, 這通常說明你需要考慮重新設(shè)計(jì)你的類.

結(jié)論: 除單元測試外, 不要使用 RTTI. 如果你發(fā)現(xiàn)自己不得不寫一些行為邏輯取決于對(duì)象類型的代碼, 考慮換一種方式判斷對(duì)象類型.

如果要實(shí)現(xiàn)根據(jù)子類類型來確定執(zhí)行不同邏輯代碼, 虛函數(shù)無疑更合適. 在對(duì)象內(nèi)部就可以處理類型識(shí)別問題.

如果要在對(duì)象外部的代碼中判斷類型, 考慮使用雙重分派方案, 如訪問者模式. 可以方便的在對(duì)象本身之外確定類的類型.

如果你認(rèn)為上面的方法你真的掌握不了, 你可以使用 RTTI, 但務(wù)必請(qǐng)三思 :-) . 不要試圖手工實(shí)現(xiàn)一個(gè)貌似 RTTI 的替代方案, 我們反對(duì)使用 RTTI 的理由, 同樣適用于那些在類型繼承體系上使用類型標(biāo)簽的替代方案.

類型轉(zhuǎn)換

Tip
    使用 C++ 的類型轉(zhuǎn)換, 如 ``static_cast<>()``. 不要使用 ``int y = (int)x`` 或 ``int y = int(x)`` 等轉(zhuǎn)換方式;

定義: C++ 采用了有別于 C 的類型轉(zhuǎn)換機(jī)制, 對(duì)轉(zhuǎn)換操作進(jìn)行歸類.

優(yōu)點(diǎn): C 語言的類型轉(zhuǎn)換問題在于模棱兩可的操作; 有時(shí)是在做強(qiáng)制轉(zhuǎn)換 (如 (int)3.5), 有時(shí)是在做類型轉(zhuǎn)換 (如 (int)"hello"). 另外, C++ 的類型轉(zhuǎn)換在查找時(shí)更醒目.

缺點(diǎn): 惡心的語法.

結(jié)論: 不要使用 C 風(fēng)格類型轉(zhuǎn)換. 而應(yīng)該使用 C++ 風(fēng)格.

  • static_cast 替代 C 風(fēng)格的值轉(zhuǎn)換, 或某個(gè)類指針需要明確的向上轉(zhuǎn)換為父類指針時(shí).
  • const_cast 去掉 const 限定符.
  • reinterpret_cast 指針類型和整型或其它指針之間進(jìn)行不安全的相互轉(zhuǎn)換. 僅在你對(duì)所做一切了然于心時(shí)使用.
  • dynamic_cast 測試代碼以外不要使用. 除非是單元測試, 如果你需要在運(yùn)行時(shí)確定類型信息, 說明有 設(shè)計(jì)缺陷 <design flaw>_.

Tip
    只在記錄日志時(shí)使用流.

定義: 流用來替代 printf()scanf().

優(yōu)點(diǎn): 有了流, 在打印時(shí)不需要關(guān)心對(duì)象的類型. 不用擔(dān)心格式化字符串與參數(shù)列表不匹配 (雖然在 gcc 中使用 printf 也不存在這個(gè)問題). 流的構(gòu)造和析構(gòu)函數(shù)會(huì)自動(dòng)打開和關(guān)閉對(duì)應(yīng)的文件.

缺點(diǎn): 流使得 pread() 等功能函數(shù)很難執(zhí)行. 如果不使用 printf 風(fēng)格的格式化字符串, 某些格式化操作 (尤其是常用的格式字符串 %.*s) 用流處理性能是很低的. 流不支持字符串操作符重新排序 (%1s), 而這一點(diǎn)對(duì)于軟件國際化很有用.

結(jié)論: 不要使用流, 除非是日志接口需要. 使用 printf 之類的代替.

使用流還有很多利弊, 但代碼一致性勝過一切. 不要在代碼中使用流.

拓展討論: 對(duì)這一條規(guī)則存在一些爭論, 這兒給出點(diǎn)深層次原因. 回想一下唯一性原則 (Only One Way): 我們希望在任何時(shí)候都只使用一種確定的 I/O 類型, 使代碼在所有 I/O 處都保持一致. 因此, 我們不希望用戶來決定是使用流還是 printf + read/write. 相反, 我們應(yīng)該決定到底用哪一種方式. 把日志作為特例是因?yàn)槿罩臼且粋€(gè)非常獨(dú)特的應(yīng)用, 還有一些是歷史原因.

流的支持者們主張流是不二之選, 但觀點(diǎn)并不是那么清晰有力. 他們指出的流的每個(gè)優(yōu)勢也都是其劣勢. 流最大的優(yōu)勢是在輸出時(shí)不需要關(guān)心打印對(duì)象的類型. 這是一個(gè)亮點(diǎn). 同時(shí), 也是一個(gè)不足: 你很容易用錯(cuò)類型, 而編譯器不會(huì)報(bào)警. 使用流時(shí)容易造成的這類錯(cuò)誤:

            cout << this;   // Prints the address
            cout << *this;  // Prints the contents

由于 << 被重載, 編譯器不會(huì)報(bào)錯(cuò). 就因?yàn)檫@一點(diǎn)我們反對(duì)使用操作符重載.

有人說 printf 的格式化丑陋不堪, 易讀性差, 但流也好不到哪兒去. 看看下面兩段代碼吧, 實(shí)現(xiàn)相同的功能, 哪個(gè)更清晰?

            cerr << "Error connecting to '" << foo->bar()->hostname.first
                 << ":" << foo->bar()->hostname.second << ": " << strerror(errno);

            fprintf(stderr, "Error connecting to '%s:%u: %s",
                    foo->bar()->hostname.first, foo->bar()->hostname.second,
                    strerror(errno));

你可能會(huì)說, "把流封裝一下就會(huì)比較好了", 這兒可以, 其他地方呢? 而且不要忘了, 我們的目標(biāo)是使語言更緊湊, 而不是添加一些別人需要學(xué)習(xí)的新裝備.

每一種方式都是各有利弊, "沒有最好, 只有更適合". 簡單性原則告誡我們必須從中選擇其一, 最后大多數(shù)決定采用 printf + read/write.

前置自增和自減

Tip
    對(duì)于迭代器和其他模板對(duì)象使用前綴形式 (``++i``) 的自增, 自減運(yùn)算符.

定義: 對(duì)于變量在自增 (++ii++) 或自減 (--ii--) 后表達(dá)式的值又沒有沒用到的情況下, 需要確定到底是使用前置還是后置的自增 (自減).

優(yōu)點(diǎn): 不考慮返回值的話, 前置自增 (++i) 通常要比后置自增 (i++) 效率更高. 因?yàn)楹笾米栽?(或自減) 需要對(duì)表達(dá)式的值 i 進(jìn)行一次拷貝. 如果 i 是迭代器或其他非數(shù)值類型, 拷貝的代價(jià)是比較大的. 既然兩種自增方式實(shí)現(xiàn)的功能一樣, 為什么不總是使用前置自增呢?

缺點(diǎn): 在 C 開發(fā)中, 當(dāng)表達(dá)式的值未被使用時(shí), 傳統(tǒng)的做法是使用后置自增, 特別是在 for 循環(huán)中. 有些人覺得后置自增更加易懂, 因?yàn)檫@很像自然語言, 主語 (i) 在謂語動(dòng)詞 (++) 前.

結(jié)論: 對(duì)簡單數(shù)值 (非對(duì)象), 兩種都無所謂. 對(duì)迭代器和模板類型, 使用前置自增 (自減).

const 的使用

Tip
    我們強(qiáng)烈建議你在任何可能的情況下都要使用 ``const``.

定義: 在聲明的變量或參數(shù)前加上關(guān)鍵字 const 用于指明變量值不可被篡改 (如 const int foo ). 為類中的函數(shù)加上 const 限定符表明該函數(shù)不會(huì)修改類成員變量的狀態(tài) (如 class Foo { int Bar(char c) const; };).

優(yōu)點(diǎn): 大家更容易理解如何使用變量. 編譯器可以更好地進(jìn)行類型檢測, 相應(yīng)地, 也能生成更好的代碼. 人們對(duì)編寫正確的代碼更加自信, 因?yàn)樗麄冎浪{(diào)用的函數(shù)被限定了能或不能修改變量值. 即使是在無鎖的多線程編程中, 人們也知道什么樣的函數(shù)是安全的.

缺點(diǎn): const 是入侵性的: 如果你向一個(gè)函數(shù)傳入 const 變量, 函數(shù)原型聲明中也必須對(duì)應(yīng) const 參數(shù) (否則變量需要 const_cast 類型轉(zhuǎn)換), 在調(diào)用庫函數(shù)時(shí)顯得尤其麻煩.

結(jié)論: const 變量, 數(shù)據(jù)成員, 函數(shù)和參數(shù)為編譯時(shí)類型檢測增加了一層保障; 便于盡早發(fā)現(xiàn)錯(cuò)誤. 因此, 我們強(qiáng)烈建議在任何可能的情況下使用 const:

  • 如果函數(shù)不會(huì)修改傳入的引用或指針類型參數(shù), 該參數(shù)應(yīng)聲明為 const.
  • 盡可能將函數(shù)聲明為 const. 訪問函數(shù)應(yīng)該總是 const. 其他不會(huì)修改任何數(shù)據(jù)成員, 未調(diào)用非 const 函數(shù), 不會(huì)返回?cái)?shù)據(jù)成員非 const 指針或引用的函數(shù)也應(yīng)該聲明成 const.
  • 如果數(shù)據(jù)成員在對(duì)象構(gòu)造之后不再發(fā)生變化, 可將其定義為 const.

然而, 也不要發(fā)了瘋似的使用 const. 像 const int * const * const x; 就有些過了, 雖然它非常精確的描述了常量 x. 關(guān)注真正有幫助意義的信息: 前面的例子寫成 const int** x 就夠了.

關(guān)鍵字 mutable 可以使用, 但是在多線程中是不安全的, 使用時(shí)首先要考慮線程安全.

const 的位置: 有人喜歡 int const *foo 形式, 不喜歡 const int* foo, 他們認(rèn)為前者更一致因此可讀性也更好: 遵循了 const 總位于其描述的對(duì)象之后的原則. 但是一致性原則不適用于此, "不要過度使用" 的聲明可以取消大部分你原本想保持的一致性. 將 const 放在前面才更易讀, 因?yàn)樵谧匀徽Z言中形容詞 (const) 是在名詞 (int) 之前.

這是說, 我們提倡但不強(qiáng)制 const 在前. 但要保持代碼的一致性! (yospaly 注: 也就是不要在一些地方把 const 寫在類型前面, 在其他地方又寫在后面, 確定一種寫法, 然后保持一致.)

整型

Tip
    C++ 內(nèi)建整型中, 僅使用 ``int``. 如果程序中需要不同大小的變量, 可以使用 ``<stdint.h>`` 中長度精確的整型, 如 ``int16_t``.

定義: C++ 沒有指定整型的大小. 通常人們假定 short 是 16 位, int是 32 位, long 是 32 位, long long 是 64 位.

優(yōu)點(diǎn): 保持聲明統(tǒng)一.

缺點(diǎn): C++ 中整型大小因編譯器和體系結(jié)構(gòu)的不同而不同.

結(jié)論: <stdint.h> 定義了 int16_t, uint32_t, int64_t 等整型, 在需要確保整型大小時(shí)可以使用它們代替 short, unsigned long long 等. 在 C 整型中, 只使用 int. 在合適的情況下, 推薦使用標(biāo)準(zhǔn)類型如 size_tptrdiff_t.

如果已知整數(shù)不會(huì)太大, 我們常常會(huì)使用 int, 如循環(huán)計(jì)數(shù). 在類似的情況下使用原生類型 int. 你可以認(rèn)為 int 至少為 32 位, 但不要認(rèn)為它會(huì)多于 32 位. 如果需要 64 位整型, 用 int64_tuint64_t.

對(duì)于大整數(shù), 使用 int64_t.

不要使用 uint32_t 等無符號(hào)整型, 除非你是在表示一個(gè)位組而不是一個(gè)數(shù)值, 或是你需要定義二進(jìn)制補(bǔ)碼溢出. 尤其是不要為了指出數(shù)值永不會(huì)為負(fù), 而使用無符號(hào)類型. 相反, 你應(yīng)該使用斷言來保護(hù)數(shù)據(jù).

關(guān)于無符號(hào)整數(shù): 有些人, 包括一些教科書作者, 推薦使用無符號(hào)類型表示非負(fù)數(shù). 這種做法試圖達(dá)到自我文檔化. 但是, 在 C 語言中, 這一優(yōu)點(diǎn)被由其導(dǎo)致的 bug 所淹沒. 看看下面的例子:

            for (unsigned int i = foo.Length()-1; i >= 0; --i) ...

上述循環(huán)永遠(yuǎn)不會(huì)退出! 有時(shí) gcc 會(huì)發(fā)現(xiàn)該 bug 并報(bào)警, 但大部分情況下都不會(huì). 類似的 bug 還會(huì)出現(xiàn)在比較有符合變量和無符號(hào)變量時(shí). 主要是 C 的類型提升機(jī)制會(huì)致使無符號(hào)類型的行為出乎你的意料.

因此, 使用斷言來指出變量為非負(fù)數(shù), 而不是使用無符號(hào)型!

64 位下的可移植性

Tip
    代碼應(yīng)該對(duì) 64 位和 32 位系統(tǒng)友好. 處理打印, 比較, 結(jié)構(gòu)體對(duì)齊時(shí)應(yīng)切記:
  • 對(duì)于某些類型, printf() 的指示符在 32 位和 64 位系統(tǒng)上可移植性不是很好. C99 標(biāo)準(zhǔn)定義了一些可移植的格式化指示符. 不幸的是, MSVC 7.1 并非全部支持, 而且標(biāo)準(zhǔn)中也有所遺漏, 所以有時(shí)我們不得不自己定義一個(gè)丑陋的版本 (頭文件 inttypes.h 仿標(biāo)準(zhǔn)風(fēng)格):
        // printf macros for size_t, in the style of inttypes.h
        #ifdef _LP64
        #define __PRIS_PREFIX "z"
        #else
        #define __PRIS_PREFIX
        #endif

        // Use these macros after a % in a printf format string
        // to get correct 32/64 bit behavior, like this:
        // size_t size = records.size();
        // printf("%"PRIuS"\n", size);
        #define PRIdS __PRIS_PREFIX "d"
        #define PRIxS __PRIS_PREFIX "x"
        #define PRIuS __PRIS_PREFIX "u"
        #define PRIXS __PRIS_PREFIX "X"
        #define PRIoS __PRIS_PREFIX "o"
類型 不要使用 使用 備注
void *(或其他指針類型) %lx %p
int64_t %qd, %lld %"PRId64"
uint64_t %qu, %llu, %llx %"PRIu64", %"PRIx64"
size_t %u %"PRIuS", %"PRIxS" C99 規(guī)定 %zu
ptrdiff_t %d %"PRIdS" C99 規(guī)定 %zd

注意 PRI* 宏會(huì)被編譯器擴(kuò)展為獨(dú)立字符串. 因此如果使用非常量的格式化字符串, 需要將宏的值而不是宏名插入格式中. 使用 PRI* 宏同樣可以在 % 后包含長度指示符. 例如, printf("x = %30"PRIuS"\n", x) 在 32 位 Linux 上將被展開為 printf("x = %30" "u" "\n", x), 編譯器當(dāng)成 printf("x = %30u\n", x) 處理 (yospaly 注: 這在 MSVC 6.0 上行不通, VC 6 編譯器不會(huì)自動(dòng)把引號(hào)間隔的多個(gè)字符串連接一個(gè)長字符串).

  • 記住 sizeof(void *) != sizeof(int). 如果需要一個(gè)指針大小的整數(shù)要用 intptr_t.

  • 你要非常小心的對(duì)待結(jié)構(gòu)體對(duì)齊, 尤其是要持久化到磁盤上的結(jié)構(gòu)體 (yospaly 注: 持久化 - 將數(shù)據(jù)按字節(jié)流順序保存在磁盤文件或數(shù)據(jù)庫中). 在 64 位系統(tǒng)中, 任何含有 int64_t/uint64_t 成員的類/結(jié)構(gòu)體, 缺省都以 8 字節(jié)在結(jié)尾對(duì)齊. 如果 32 位和 64 位代碼要共用持久化的結(jié)構(gòu)體, 需要確保兩種體系結(jié)構(gòu)下的結(jié)構(gòu)體對(duì)齊一致. 大多數(shù)編譯器都允許調(diào)整結(jié)構(gòu)體對(duì)齊. gcc 中可使用 __attribute__((packed)). MSVC 則提供了 #pragma pack()__declspec(align()) (YuleFox 注, 解決方案的項(xiàng)目屬性里也可以直接設(shè)置).

  • 創(chuàng)建 64 位常量時(shí)使用 LL 或 ULL 作為后綴, 如:
        int64_t my_value = 0×123456789LL;
        uint64_t my_mask = 3ULL << 48;
  • 如果你確實(shí)需要 32 位和 64 位系統(tǒng)具有不同代碼, 可以使用 #ifdef _LP64 指令來切分 32/64 位代碼. (盡量不要這么做, 如果非用不可, 盡量使修改局部化)

預(yù)處理宏

Tip
    使用宏時(shí)要非常謹(jǐn)慎, 盡量以內(nèi)聯(lián)函數(shù), 枚舉和常量代替之.

宏意味著你和編譯器看到的代碼是不同的. 這可能會(huì)導(dǎo)致異常行為, 尤其因?yàn)楹昃哂腥肿饔糜?

值得慶幸的是, C++ 中, 宏不像在 C 中那么必不可少. 以往用宏展開性能關(guān)鍵的代碼, 現(xiàn)在可以用內(nèi)聯(lián)函數(shù)替代. 用宏表示常量可被 const 變量代替. 用宏 "縮寫" 長變量名可被引用代替. 用宏進(jìn)行條件編譯... 這個(gè), 千萬別這么做, 會(huì)令測試更加痛苦 (#define 防止頭文件重包含當(dāng)然是個(gè)特例).

宏可以做一些其他技術(shù)無法實(shí)現(xiàn)的事情, 在一些代碼庫 (尤其是底層庫中) 可以看到宏的某些特性 (如用 # 字符串化, 用 ## 連接等等). 但在使用前, 仔細(xì)考慮一下能不能不使用宏達(dá)到同樣的目的.

下面給出的用法模式可以避免使用宏帶來的問題; 如果你要宏, 盡可能遵守:

  • 不要在 .h 文件中定義宏.
  • 在馬上要使用時(shí)才進(jìn)行 #define, 使用后要立即 #undef.
  • 不要只是對(duì)已經(jīng)存在的宏使用#undef,選擇一個(gè)不會(huì)沖突的名稱;
  • 不要試圖使用展開后會(huì)導(dǎo)致 C++ 構(gòu)造不穩(wěn)定的宏, 不然也至少要附上文檔說明其行為.

0 和 NULL

Tip
    整數(shù)用 ``0``, 實(shí)數(shù)用 ``0.0``, 指針用 ``NULL``, 字符 (串) 用 ``'\0'``.

整數(shù)用 0, 實(shí)數(shù)用 0.0, 這一點(diǎn)是毫無爭議的.

對(duì)于指針 (地址值), 到底是用 0 還是 NULL, Bjarne Stroustrup 建議使用最原始的 0. 我們建議使用看上去像是指針的 NULL, 事實(shí)上一些 C++ 編譯器 (如 gcc 4.1.0) 對(duì) NULL 進(jìn)行了特殊的定義, 可以給出有用的警告信息, 尤其是 sizeof(NULL)sizeof(0) 不相等的情況.

字符 (串) 用 '\0', 不僅類型正確而且可讀性好.

sizeof

Tip
    盡可能用 ``sizeof(varname)`` 代替 ``sizeof(type)``.

使用 sizeof(varname) 是因?yàn)楫?dāng)代碼中變量類型改變時(shí)會(huì)自動(dòng)更新. 某些情況下 sizeof(type) 或許有意義, 但還是要盡量避免, 因?yàn)樗鼤?huì)導(dǎo)致變量類型改變后不能同步.

        Struct data;
        Struct data; memset(&data, 0, sizeof(data));
     Warning

            memset(&data, 0, sizeof(Struct));

Boost 庫

Tip
    只使用 Boost 中被認(rèn)可的庫.

定義: Boost 庫集 <http://www.boost.org/>_ 是一個(gè)廣受歡迎, 經(jīng)過同行鑒定, 免費(fèi)開源的 C++ 庫集.

優(yōu)點(diǎn): Boost代碼質(zhì)量普遍較高, 可移植性好, 填補(bǔ)了 C++ 標(biāo)準(zhǔn)庫很多空白, 如型別的特性, 更完善的綁定器, 更好的智能指針, 同時(shí)還提供了 TR1 (標(biāo)準(zhǔn)庫擴(kuò)展) 的實(shí)現(xiàn).

缺點(diǎn): 某些 Boost 庫提倡的編程實(shí)踐可讀性差, 比如元編程和其他高級(jí)模板技術(shù), 以及過度 "函數(shù)化" 的編程風(fēng)格.

結(jié)論: 為了向閱讀和維護(hù)代碼的人員提供更好的可讀性, 我們只允許使用 Boost 一部分經(jīng)認(rèn)可的特性子集. 目前允許使用以下庫:

  • Compressed Pair <http://www.boost.org/libs/utility/compressed_pair.htm>_ : boost/compressed_pair.hpp

  • Pointer Container <http://www.boost.org/libs/ptr_container/>_ : boost/ptr_container (序列化除外)

  • Array <http://www.boost.org/libs/array/>_ : boost/array.hpp

  • The Boost Graph Library (BGL) <http://www.boost.org/libs/graph/>_ : boost/graph (序列化除外)

  • Property Map <http://www.boost.org/libs/property_map/>_ : boost/property_map.hpp

  • Iterator <http://www.boost.org/libs/iterator/>_ 中處理迭代器定義的部分 : boost/iterator/iterator_adaptor.hpp, boost/iterator/iterator_facade.hpp, 以及 boost/function_output_iterator.hpp

我們正在積極考慮增加其它 Boost 特性, 所以列表中的規(guī)則將不斷變化.

上一篇:注釋下一篇: