第一章介紹 C++ 的一些基本方式。
1. 視 C++ 為一個(gè)語(yǔ)言聯(lián)邦
現(xiàn)在 C++ 已經(jīng)是個(gè)多重泛型編程語(yǔ)言,同時(shí)支持過(guò)程、面向?qū)ο蟆⒑瘮?shù)、泛型、元編程形式。將其視為語(yǔ)言聯(lián)邦,主要有四個(gè)次語(yǔ)言:
-
C
區(qū)塊、語(yǔ)句、預(yù)處理、內(nèi)置數(shù)據(jù)類型、數(shù)組、指針都來(lái)自C。此時(shí)傳值比傳引用高效。 -
Object-Oriented C++
類(構(gòu)造、析構(gòu)函數(shù))、封裝、繼承、多態(tài)、虛函數(shù)。傳引用比傳值高效。
-Template C++
新的編程范型:模板元編程 -
STL
容器、迭代器、算法、函數(shù)對(duì)象。傳值更高效。
總結(jié):
C++ 高效編程守則視狀況而變化,取決于使用 C++ 的哪一部分。
2. 盡量以const, enum, inline 替換 #define
寧可以編譯器替換預(yù)處理器。#define不被視為語(yǔ)言的一部分,所以編譯出錯(cuò)時(shí)難以查找。如:
#define ASPECT_RATIO 1.653
應(yīng)換為
const double AspectRatio = 1.653
此時(shí)AspectRatio會(huì)進(jìn)入編譯器的記號(hào)表,而且對(duì)于浮點(diǎn)數(shù)使用常量可能比 #define 碼量更小。常量替換 #define 的兩種特殊情況:
-
定義常量指針
例如定義一個(gè)常量的char *-based字符串:
改為 string 更合適:
-
class專屬常量
為了將常量的作用域限制在class中,需將其設(shè)為class的一個(gè)成員;要確保其只有一份實(shí)體,需聲明為static成員。
另外需要提供定義式才能取地址:一般形式為:但是當(dāng)這個(gè)常量用于指定數(shù)組大小,可改用“the enum hack”: 一個(gè)屬于枚舉類型的數(shù)值可權(quán)充 ints 被使用。
enum比較像#define, 取enum 地址不合法,但是取const地址合法。enums 與 #defines一樣絕不會(huì)導(dǎo)致非必要的內(nèi)存分配。



產(chǎn)生一整群函數(shù),每個(gè)函數(shù)接受兩個(gè)同型對(duì)象。
總結(jié)
- 對(duì)于單純常量,最好以const對(duì)象或enums替換#define
- 對(duì)于形如函數(shù)的宏,最好改用inline函數(shù)替換#define
3. 盡可能使用const
關(guān)鍵詞const 用于指定一個(gè)不該被改動(dòng)的對(duì)象,加上這個(gè)約束后編譯器會(huì)強(qiáng)制保證實(shí)施。所以只要某個(gè)值確定應(yīng)該不變,就應(yīng)該加上以得到編譯器的幫助。
當(dāng)const和指針組合時(shí):

- const出現(xiàn)在星號(hào)左邊——被指物是常量
- const出現(xiàn)在星號(hào)右邊——指針自身是常量
- 兩邊都有——被指物和指針都是常量
以相對(duì)指針的位置來(lái)判斷,所以以下兩種寫法相同:

當(dāng)const和STL迭代器組合時(shí):迭代器的作用就像是 T* 指針。const 迭代器 = T* const 指針,表示這個(gè)迭代器不得指向不同的東西,但所指對(duì)象的值可變。如:

當(dāng)const和函數(shù)聲明組合時(shí):返回一個(gè)常量值


const成員函數(shù)
const用于成員函數(shù)是為了保證該函數(shù)可作用于const對(duì)象。這類函數(shù)重要的理由:
- 使class接口比較容易被理解
- 使操作const對(duì)象成為可能



錯(cuò)誤在于企圖對(duì)由const版的operator[]返回的const char &施行賦值。
const成員函數(shù)的兩種概念:
-
bitwise constness認(rèn)為成員函數(shù)只有在不更改對(duì)象的任何成員變量時(shí)才能稱為const。所以const成員函數(shù)不能更改對(duì)象內(nèi)任何non-static成員變量。
但一個(gè)更改了指針?biāo)肝锏某蓡T函數(shù)雖然不算const,若只有指針屬于對(duì)象,那么不會(huì)引發(fā)編譯錯(cuò)誤。

最終改變了常量對(duì)象的值。
-
logical constness
由于存在以上的錯(cuò)誤,這里主張一個(gè)const成員函數(shù)可以修改它所處理的對(duì)象內(nèi)的某些bits。如:為了可以修改這兩個(gè)數(shù)據(jù),可用mutable釋放non-static成員變量的bitwise constness約束。
在const和non-const成員函數(shù)中避免重復(fù)
有時(shí)候成員函數(shù)需要進(jìn)行多個(gè)步驟,這就造成const和non-const成員函數(shù)存在大量重復(fù):

值得注意的是反向調(diào)用是錯(cuò)誤的。用const調(diào)用non-const冒著對(duì)象被改的風(fēng)險(xiǎn)。
總結(jié)
- 將某些東西聲明為const可幫助編譯器偵測(cè)出錯(cuò)誤用法。const可被施加于任何作用域內(nèi)的對(duì)象、函數(shù)參數(shù)、函數(shù)返回類型、成員函數(shù)本體。
- 編譯器強(qiáng)制實(shí)施bitwise constness,但編寫程序時(shí)應(yīng)使用概念上的常量性。
- 當(dāng)const和non-const成員函數(shù)有著實(shí)質(zhì)等價(jià)的實(shí)現(xiàn)時(shí),令non-const版本調(diào)用const版本可避免代碼重復(fù)。
4. 確定對(duì)象使用前已先被初始化
C++ 變量在聲名時(shí)有時(shí)會(huì)被初始化有時(shí)不會(huì),需手工完成。


此時(shí)省去了default構(gòu)造的過(guò)程,都進(jìn)行copy構(gòu)造。也可在列用default構(gòu)造:

而且如果成員變量是const或reference就一定需要初值,不能被賦值。所以最簡(jiǎn)單的做法就是:總是使用成員初值列。
C++ 也有固定的成員初始化次序:基類早于子類,類的成員變量總是以其聲明次序被初始化。
non-local static對(duì)象的初始化次序
所謂static對(duì)象,壽命從被構(gòu)造出來(lái)到程序結(jié)束為止。也包括global對(duì)象,其中函數(shù)內(nèi)的static對(duì)象為local的。它們的析構(gòu)函數(shù)會(huì)在main函數(shù)結(jié)束時(shí)自動(dòng)調(diào)用。



此時(shí)就必須保證tfs在tempDir之前被初始化。正確的做法是 將每個(gè)non-local static對(duì)象搬到自己的專屬函數(shù)內(nèi),即用local static對(duì)象替換non-local static對(duì)象。這么做的基礎(chǔ)是:C++ 保證函數(shù)內(nèi)static對(duì)象會(huì)在該函數(shù)被調(diào)用期間首次遇上該對(duì)象的定義式時(shí)被初始化。如:

現(xiàn)在使用函數(shù)返回的指向static對(duì)象的reference,而不再使用static對(duì)象自身。
所以為了避免初始化之前使用對(duì)象需要做:
- 手工初始化內(nèi)置型non-member對(duì)象
- 使用成員初值列對(duì)付對(duì)象的所有成分
- 在初始化次序不確定時(shí)用函數(shù)返回的指向static對(duì)象的reference。
總結(jié)
- 為內(nèi)置型對(duì)象進(jìn)行手工初始化,C++不保證初始化。
- 構(gòu)造函數(shù)最好使用成員初值列,不要再構(gòu)造函數(shù)本體內(nèi)用賦值操作。初值列列出的變量次序應(yīng)與聲明次序同
- 為免除“跨編譯單元的初始化次序”問(wèn)題,用local static對(duì)象替換non-local static對(duì)象。








