本書從《C++核心準(zhǔn)則》(C++ Core Guidelines)中精心挑選了 30 條準(zhǔn)則進(jìn)行細(xì)致、深入的講解。內(nèi)容涵蓋C++語言最主要的方面,如類型系統(tǒng)、面向?qū)ο蟆⒛0搴驮幊、錯誤處理、程序性能、常量性等,其間又恰如其分地穿插了編碼風(fēng)格、設(shè)計模式等主題。書中匯集了作者數(shù)十年職業(yè)生涯的經(jīng)驗(yàn)和一些有趣的示例,除了深刻的見解,行文也充滿了趣味性。作者試圖通過這種突出重點(diǎn)、以點(diǎn)帶面的方式幫助讀者了解并學(xué)習(xí)《C++核心準(zhǔn)則》,進(jìn)而更深入地掌握 C++這門編程語言,特別是它的"現(xiàn)代”形態(tài)。本書適合各種經(jīng)驗(yàn)水平的 C++開發(fā)者閱讀。
J. Guy Davidson于1980年通過Acorn Atom首次接觸編程。他青少年時代的大部分時間都在各種家用電腦上編寫游戲。后來,他從蘇塞克斯大學(xué)獲得了數(shù)學(xué)學(xué)位,開始涉足戲劇,還在一個靈魂樂隊中擔(dān)任鍵盤手。20世紀(jì)90年代初,他決定編寫演示程序,并于1997年開始在Codemasters的倫敦辦公室工作,從此進(jìn)入游戲行業(yè)。1999年,Davidson加入了Creative Assembly,現(xiàn)在是那里的工程實(shí)踐主管。他主要負(fù)責(zé)《全面戰(zhàn)爭》(Total War)系列游戲的工作,整理早期的游戲目錄,以及提升工程團(tuán)隊成員的編程水平。他是IGGI咨詢委員會、BSI C++小組和ISO C++委員會的委員,還是ACCU(Association of C/C++ Users,C/C++用戶協(xié)會)負(fù)責(zé)C++標(biāo)準(zhǔn)相關(guān)事宜的成員,并在ACCU的編程委員會任職。他是#include Discord服務(wù)器的管理員。他擔(dān)任多個組織的行為準(zhǔn)則負(fù)責(zé)人,在C++會議和各種聚會上發(fā)言,特別是關(guān)于在標(biāo)準(zhǔn)庫中增加線性代數(shù)的議題。Kate Gregory接觸編程、與幾位最親密的朋友結(jié)識,以及丈夫的相識,都發(fā)生在1977年的滑鐵盧大學(xué),所有這些她從未后悔過。她的學(xué)位是化學(xué)工程,這正說明你很難從一個人的學(xué)位中看出什么。她在加拿大安大略省鄉(xiāng)下的地下室有一個小房間,里面放著一些古老的計算機(jī):PET、C64、手工焊接的6502系統(tǒng)等,她把這些作為那個單純年代的紀(jì)念品。自1986年起,她與丈夫一起經(jīng)營Gregory咨詢公司,幫助世界各地的客戶更好地開展業(yè)務(wù)。Kate曾在五個大洲做過主題演講,愛發(fā)掘一些改變認(rèn)知的真相,然后與人分享,她還投入大量的時間在各種C++活動中做志愿者。其中“#include ”是她的最愛,該社區(qū)正在改變這個行業(yè),使其更受歡迎也更具包容性。他們的Discord服務(wù)器是一處溫馨的場所,初學(xué)者在那里可以學(xué)習(xí)C++,也可以為WG21合寫文章以改變我們所使用的編程語言,或者做任何介于兩者之間的事情。
王江平,Autodesk軟件開發(fā)工程師,畢業(yè)于同濟(jì)大學(xué)/上海交通大學(xué),愛編程,愛讀書,愛翻譯,愛C++。譯有《C#3.0設(shè)計模式》、《敏捷開發(fā)的藝術(shù)》、《軟件開發(fā)者路線圖》、《Java語言精粹》、《Python計算與編程實(shí)踐》、《Cucumber:行為驅(qū)動開發(fā)指南》等書籍。(微博@steedhorse,歡迎交流。)
第1章 避重就輕不可取 1
1.1 P.2:使用ISO標(biāo)準(zhǔn)C++編寫代碼 2
什么是ISO標(biāo)準(zhǔn)C++ 2
封裝差異 4
了解以前的用法 8
緊跟標(biāo)準(zhǔn)的發(fā)展 9
1.2 F.51:在有選擇的情況下,優(yōu)先使用默認(rèn)參數(shù)而非重載 12
引言 12
改進(jìn)抽象概念:是增加參數(shù)還是重載 13
微妙的重載解決 14
回到示例代碼 16
默認(rèn)參數(shù)天然的明確性 18
函數(shù)重載的替代方案 19
有時必須重載 19
小結(jié) 20
1.3 C.45:不要定義僅初始化數(shù)據(jù)成員的默認(rèn)構(gòu)造函數(shù),而應(yīng)使用類內(nèi)成員
?初始化 21
為什么要有默認(rèn)構(gòu)造函數(shù) 21
你是怎樣初始化數(shù)據(jù)成員的 22
兩個人維護(hù)一個類時會怎樣 25
小結(jié) 27
1.4 C.131:避免平凡的get和set函數(shù) 28
一種古老的慣用法 28
抽象 29
單純的封裝 31
類不變式 34
名詞和動詞 36
小結(jié) 37
1.5 ES.10:每條語句只聲明一個名字 38
我來引入你 38
向后兼容 40
寫出更加清晰的聲明 42
結(jié)構(gòu)式綁定 43
小結(jié) 44
1.6 NR.2:不強(qiáng)求函數(shù)只用一條return語句 45
規(guī)則會演化 45
確保資源得到清理 47
使用RAII 50
編寫好的函數(shù) 52
小結(jié) 54
第2章 不要傷害自己 55
2.1 P.11:將凌亂的構(gòu)造封裝起來,而不是使其散布于代碼中 56
“一口吞”式做法 56
封裝一種凌亂的構(gòu)造意味著什么 58
語言的目的和抽象的本質(zhì) 60
抽象的層次 63
通過重構(gòu)和分割實(shí)現(xiàn)抽象 64
小結(jié) 65
2.2 I.23:盡量減少函數(shù)參數(shù) 66
他們應(yīng)該掙多少 66
通過抽象簡化問題 68
盡可能少,但不要更少 70
現(xiàn)實(shí)例子 72
小結(jié) 73
2.3 I.26:使用C風(fēng)格子集獲取跨編譯器的ABI 74
創(chuàng)建程序庫 74
什么是ABI 75
最小的C風(fēng)格子集 77
異常的傳播 79
小結(jié) 80
2.4 C.47:按聲明的順序定義并初始化成員變量 82
小結(jié) 90
2.5 CP.3:盡量減少可寫數(shù)據(jù)的顯式共享 91
傳統(tǒng)執(zhí)行模型 91
等等,不止這些 93
避免死鎖和數(shù)據(jù)競爭 95
拋開鎖和互斥體 97
小結(jié) 100
2.6 T.120:只在真正需要時使用模板元編程 101
std::enable_if => requires 108
小結(jié) 112
第3章 別再使用 113
3.1 I.11:切勿通過原生指針(T*)或引用(T&)轉(zhuǎn)移所有權(quán) 114
使用自由存儲區(qū) 114
智能指針的性能成本 117
使用未修飾的引用語義 118
gsl::owner 119
小結(jié) 121
3.2 I.3:避免使用單例 122
全局對象是不好的 122
單例設(shè)計模式 123
靜態(tài)初始化順序的尷尬 123
如何隱藏一個單例 125
但它就該只有一份 127
等一下…… 128
小結(jié) 130
3.3 C.90:使用構(gòu)造函數(shù)和賦值運(yùn)算符,而不是memset和memcpy 131
追求最優(yōu)性能 131
構(gòu)造函數(shù)的巨大開銷 132
最簡單的類 133
C++標(biāo)準(zhǔn)到底在說什么 135
那么,memcpy呢 137
永遠(yuǎn)不要低估編譯器 139
小結(jié) 140
3.4 ES.50:不要用強(qiáng)制轉(zhuǎn)換去除const限定符 141
故事時間 141
處理更大的數(shù)據(jù)量 142
const防火墻 143
實(shí)現(xiàn)雙接口 144
緩存和懶惰求值 146
兩種類型的const 147
const驚奇篇 149
小結(jié) 150
3.5 E.28:避免基于全局狀態(tài)(如errno)的錯誤處理 151
錯誤處理很難 151
C和errno 151
返回錯誤代碼 153
異常 154
<system_error> 154
Boost.Outcome 155
錯誤處理為何這樣難 156
隧道盡頭的光 158
小結(jié) 159
3.6 SF.7:不要在頭文件的全局作用域?qū)憉sing namespace 160
不要這樣做 160
消除歧義 161
使用using 162
符號去了哪里 163
一個更加隱蔽的問題 166
解決作用域解析運(yùn)算符的雜亂問題 168
誘惑與墮落 169
小結(jié) 169
第4章 正確使用新特性 171
4.1 F.21:優(yōu)先選擇結(jié)構(gòu)體或元組返回多個“輸出”值 172
函數(shù)簽名的形式 172
文檔和注解 173
現(xiàn)在可以返回對象了 174
也可以返回元組 177
使用非const引用傳遞和返回 179
小結(jié) 182
4.2 Enum.3:優(yōu)先選擇class枚舉而不是“普通”枚舉 183
常量 183
作用域枚舉 185
基礎(chǔ)類型 187
隱式轉(zhuǎn)換 188
小結(jié) 190
后記 190
4.3 ES.5:保持作用域短小 191
作用域的性質(zhì) 191
塊作用域 192
名字空間作用域 193
類作用域 196
函數(shù)參數(shù)作用域 198
枚舉作用域 199
模板參數(shù)作用域 200
作用域作為上下文 201
小結(jié) 202
4.4 Con.5:使用constexpr表示編譯時可以計算的值 203
從const到constexpr 203
默認(rèn)的C++ 205
使用constexpr 206
inline 210
constevel 211
constinit 212
小結(jié) 213
4.5 T.1:使用模板提高代碼的抽象層次 214
故事時間 214
提高抽象的層次 216
函數(shù)模板和抽象 218
類模板和抽象 220
命名很難 222
小結(jié) 223
4.6 T.10:為所有模板參數(shù)指定概念 224
來龍去脈 224
約束你的參數(shù) 226
如何抽象你的概念 229
通過概念分解 232
小結(jié) 233
第5章 默認(rèn)寫出好代碼 234
5.1 P.4:在理想情況下,程序應(yīng)具有靜態(tài)類型安全性 235
類型安全是C++的一項(xiàng)安全特性 235
聯(lián)合體 237
類型轉(zhuǎn)換 238
無符號數(shù) 241
緩沖區(qū)和大小 244
小結(jié) 245
5.2 P.10:優(yōu)先選擇不可變數(shù)據(jù)而不是可變數(shù)據(jù) 246
錯誤的默認(rèn)設(shè)置 246
函數(shù)聲明中的常量性 248
小結(jié) 252
5.3 I.30:封裝違反規(guī)則的部分 253
隱藏生活中不悅目的東西 253
保全體面 255
小結(jié) 260
5.4 ES.22:初始值確定后再聲明變量 261
表達(dá)式和語句的重要性 261
C風(fēng)格的聲明 262
先聲明后初始化 263
盡可能推遲的聲明 264
上下文特定功能的局部化 266
消除狀態(tài) 268
小結(jié) 270
5.5 Per.7:為促成優(yōu)化而設(shè)計 271
幀率最大化 271
離硬件更遠(yuǎn)之后 273
通過抽象進(jìn)行優(yōu)化 276
小結(jié) 278
5.6 E.6:使用RAII防止泄漏 279
確定性析構(gòu) 279
文件泄漏 281
為什么要做這些 284
似乎還是有點(diǎn)兒多:未來的可能性 286
從哪里獲得這些工具 289
后記 291
跋 293
【C++核心準(zhǔn)則精選】
P.2:使用ISO標(biāo)準(zhǔn)C++編寫代碼(1.1節(jié))[ 可以在GitHub或者GitHub Pages上找到C++ Core Guidelines,查看詳細(xì)內(nèi)容。]
P.4:在理想情況下,程序應(yīng)具有靜態(tài)類型安全性(5.1節(jié))
P.10:優(yōu)先選擇不可變數(shù)據(jù)而不是可變數(shù)據(jù)(5.2節(jié))
P.11:將凌亂的構(gòu)造封裝起來,而不是使其散布于代碼中(2.1節(jié))
I.3:避免使用單例(3.2節(jié))
I.11:切勿通過原生指針(T*)或引用(T&)轉(zhuǎn)移所有權(quán)(3.1節(jié))
I.23:盡量減少函數(shù)參數(shù)(2.2節(jié))
I.26:使用C風(fēng)格子集獲取跨編譯器的ABI(2.3節(jié))
I.30:封裝違反規(guī)則的部分(5.3節(jié))
F.21:優(yōu)先選擇結(jié)構(gòu)體或元組返回多個“輸出”值(4.1節(jié))
F.51:在有選擇的情況下,優(yōu)先使用默認(rèn)參數(shù)而非重載(1.2節(jié))
C.45:不要定義僅初始化數(shù)據(jù)成員的默認(rèn)構(gòu)造函數(shù),而應(yīng)該使用類內(nèi)成員初始化(1.3節(jié))
C.47:按聲明的順序定義并初始化成員變量(2.4節(jié))
C.90:使用構(gòu)造函數(shù)和賦值運(yùn)算符,而不是memset和memcpy(3.3節(jié))
C.131:避免平凡的get和set函數(shù)(1.4節(jié))
Enum.3:優(yōu)先選擇class枚舉而不是“普通”枚舉(4.2節(jié))
ES.5:保持作用域短。4.3節(jié))
ES.10:每條語句只聲明一個名字(1.5節(jié))
ES.22:初始值確定后再聲明變量(5.4節(jié))
ES.50:不要用強(qiáng)制轉(zhuǎn)換去除const限定符(3.4節(jié))
Per.7:為促成優(yōu)化而設(shè)計(5.5節(jié))
CP.3:盡量減少可寫數(shù)據(jù)的顯式共享(2.5節(jié))
E.6:使用RAII防止泄漏(5.6節(jié))
E.28:避免基于全局狀態(tài)(如errno)的錯誤處理(3.5節(jié))
Con.5:使用constexpr表示編譯時可以計算的值(4.4節(jié))
T.1:使用模板提高代碼的抽象層次(4.5節(jié))
T.10:為所有模板參數(shù)指定概念(4.6節(jié))
T120:只在真正需要時使用模板元編程(2.6節(jié))
SF.7:不要在頭文件的全局作用域?qū)憉sing namespace(3.6節(jié))
NR.2:不強(qiáng)求函數(shù)只用一條return語句(1.6節(jié))