C++代碼提供了足夠的靈活性,因此對于大部分工程師來說都很難把握。本文介紹了寫好C++代碼需要遵循的10個最佳實踐,并在最后提供了一個工具可以幫助我們分析C++代碼的健壯度。
1. 盡可能嘗試使用新的C++標準
到2022年,C++已經(jīng)走過了40多個年頭。新的C++標準實際上簡化了許多令人沮喪的細節(jié),提供了新的現(xiàn)代方法來改進C++代碼,但讓開發(fā)人員認識到這一點并不容易。
以內(nèi)存管理為例,這可能是C++中受到最多批評的機制。多年來,對象分配都是由new關(guān)鍵字完成的,開發(fā)人員一定得記住在代碼的某個地方調(diào)用delete?!艾F(xiàn)代C++”解決了這個問題,并促進了共享指針的使用。
2. 使用命名空間模塊化代碼
現(xiàn)代C++庫廣泛使用命名空間來模塊化代碼庫,它們利用“Namespace-by-feature”方法,按功能劃分命名空間來反映功能集,將單個特性(且僅與該特性)相關(guān)的所有內(nèi)容放到單個命名空間中。從而使得命名空間具有高內(nèi)聚性和高模塊化,并且耦合最小,緊耦合的項目被放在了一起。
Boost是按特性分組的最佳示例,其包含數(shù)千個命名空間,每個命名空間用于對特定的特性進行分組。
3. 抽象
數(shù)據(jù)抽象是C++中面向?qū)ο?a target="_blank">編程最基本和最重要的特性之一。抽象意味著只顯示基本信息而隱藏細節(jié),數(shù)據(jù)抽象指的是僅向外部世界提供關(guān)于數(shù)據(jù)的基本信息,隱藏背景細節(jié)或?qū)崿F(xiàn)。
盡管許多書籍、網(wǎng)絡(luò)資源、會議演講者和專家都推薦這種最佳實踐,但在很多項目中,這條規(guī)則仍然被忽略了,許多類的細節(jié)并沒有被隱藏。
4. 類越小越好
具有多行代碼的類型應(yīng)該被劃分為一組較小的類型。
需要很大的耐心重構(gòu)一個大的類,甚至可能需要從頭重新創(chuàng)建所有東西。以下是一些重構(gòu)建議:
BigClass中的邏輯必須被分成更小的類。這些較小的類最終可能成為嵌套在原始God Class中的私有類,God Class的實例對象由較小嵌套類的實例組成。
較小的類劃分應(yīng)該由God Class負責的多個職責驅(qū)動。要確定這些職責,通常需要查找與字段的子集強耦合的方法的子集。
如果BigClass包含的邏輯比狀態(tài)多,一個好的選擇是定義一個或幾個不包含靜態(tài)字段而只包含純靜態(tài)方法的靜態(tài)類。純靜態(tài)方法是一種只根據(jù)輸入參數(shù)計算結(jié)果的函數(shù),它不讀取或分配任何靜態(tài)或?qū)嵗侄?。純靜態(tài)方法的主要優(yōu)點是易于測試。
首先嘗試維護BigClass的接口,并委托調(diào)用新提取的類。最后,BigClass應(yīng)該是一個沒有自己邏輯的純接口,可以為了方便將其保留,也可以將其扔掉,并開始只使用新類。
單元測試可以提供幫助: 在提取方法之前為每個方法編寫測試,以確保不會破壞功能。
5. 每個類盡量提供最少的方法
包含20個以上方法的類可能很難理解和維護。
一個類有許多方法可能是實現(xiàn)了太多責任的癥狀。
也許所面對的類控制了系統(tǒng)中太多的其他類,并且已經(jīng)超出了應(yīng)有的邏輯,成為了一個無所不能的類。
6. 加強低耦合
低耦合是理想狀態(tài),可以在應(yīng)用中進行較少的更改實現(xiàn)程序的某個變更。從長遠來看,可以減少修改、添加新特性的大量時間、精力和成本。
低耦合可以通過使用抽象類或泛型類和方法來實現(xiàn)。
7. 加強高內(nèi)聚
單一責任原則規(guī)定一個類不應(yīng)該有多于一個更改的理由,這樣的類被稱為內(nèi)聚類。較高的LCOM值通??梢砸馕吨惖膬?nèi)聚性較差。有幾個LCOM指標,取值范圍為[0-1]。LCOM HS (HS代表Henderson-Sellers)取值范圍為[0-2]。LCOM HS值大于1時需要產(chǎn)生警惕。下面是計算LCOM指標:
LCOM = 1 — (sum(MF)/M*F)
LCOM HS = (M — sum(MF)/F)(M-1)
其中……
M是類中方法的數(shù)量(包括靜態(tài)方法和實例方法,它還包括構(gòu)造函數(shù)、屬性getter/setter、事件添加/刪除方法)。
F是類中實例字段的數(shù)量。
MF是類訪問特定實例字段的方法數(shù)量。
Sum(MF)是該類所有實例字段的MF之和。
這些公式背后的基本思想可以表述如下: 如果一個類的所有方法都使用它的所有實例字段,那么這個類就是完全內(nèi)聚的,這意味著sum(MF)=M*F,然后LCOM = 0和LCOMHS = 0。
LCOMHS值大于1就需要警惕了。
8. 只注釋代碼不能表達的內(nèi)容
鸚鵡學舌的代碼注釋沒有為讀者提供任何額外的東西。代碼庫中充斥著嘈雜的注釋和不正確的注釋,促使程序員忽略所有的注釋,或者采取積極的措施隱藏它們。
9. 盡量不要用重復的代碼
眾所周知,重復代碼的存在對軟件開發(fā)和維護有負面影響。實際上,一個主要缺點是,當為了修復bug或添加新特性而更改重復代碼的實例時,所有對應(yīng)的代碼必須同時更改。
產(chǎn)生重復代碼最常見的原因是復制/粘貼操作,這種情況下,相似的源代碼出現(xiàn)在兩個或多個地方。許多文章、書籍和網(wǎng)站都警告不要采用這種做法,但有時實踐這些建議并不容易,開發(fā)人員還是會選擇簡單的解決方案: 復制/粘貼大法。
使用適當?shù)墓ぞ呖梢匀菀椎膹膹椭?粘貼操作中檢測到重復代碼,但是,在某些情況下,克隆代碼很難被檢測到。
10. 不變性有助于多線程編程
基本上,如果對象在創(chuàng)建之后狀態(tài)不變,那么這個對象就是不可變(immutable)的。如果一個類的實例是不可變的,那么該類就是不可變的。
不可變對象極大簡化了并發(fā)編程,這是支持使用它的重要理由。想想看,為什么編寫適當?shù)亩嗑€程程序是一項艱巨的任務(wù)?因為同步線程訪問資源(對象或其他操作系統(tǒng)資源)是很困難的。為什么同步這些訪問很困難?因為很難保證多個線程對多個對象進行的多次寫訪問和讀訪問之間不會出現(xiàn)競爭條件。如果不再有寫訪問會怎么樣?換句話說,如果被線程訪問的對象的狀態(tài)沒有改變會怎么樣?就不再需要同步了!
關(guān)于不可變類的另一個好處是它們永遠不會違反里氏替換原則(LSP, Liskov Subtitution Principle),以下是維基百科對LSP的定義:
Liskov的行為子類型的概念定義了可變對象可替換性的概念,也就是說,如果S是T的子類型,那么程序中T類型的對象可以被替換為S類型的對象,而不改變該程序的任何期望屬性(例如,正確性)。
如果沒有公共字段,沒有可以更改其內(nèi)部數(shù)據(jù)的方法,并且派生類方法無法更改其內(nèi)部數(shù)據(jù),那么引用對象類就是不可變的。因為值不可變,所以在所有情況下都可以引用相同的對象,不需要復制構(gòu)造函數(shù)或賦值操作符。出于這個原因,建議將復制構(gòu)造函數(shù)和賦值操作符設(shè)為私有,或者從boost::noncopyable繼承,或者使用新的C++ 11特性“顯式默認和刪除特殊成員函數(shù)”[2]。
如何加強對這些最佳實踐進行檢查?
CppDepend[3]提供了名為CQLinq[4]的代碼查詢語言,可以像數(shù)據(jù)庫一樣查詢代碼庫。開發(fā)人員、設(shè)計人員和架構(gòu)師可以自定義查詢,以便輕松找到容易出現(xiàn)bug的情況。
通過CQLinq,可以結(jié)合來自代碼度量、依賴關(guān)系、API使用和其他模型的數(shù)據(jù)來定義非常高級的查詢,以匹配容易出現(xiàn)bug的情況。
例如,分析clang源代碼后,可以檢測到大類:
檢測到有大量方法的類:
或者檢測到內(nèi)聚性較差的類:
審核編輯:郭婷
-
C++
+關(guān)注
關(guān)注
22文章
2114瀏覽量
73890 -
代碼
+關(guān)注
關(guān)注
30文章
4837瀏覽量
69126
發(fā)布評論請先 登錄
相關(guān)推薦
基于OpenHarmony標準系統(tǒng)的C++公共基礎(chǔ)類庫案例:ThreadPoll
![基于OpenHarmony標準系統(tǒng)的<b class='flag-5'>C++</b>公共基礎(chǔ)類庫案例:ThreadPoll](https://file.elecfans.com/web2/M00/26/21/pYYBAGG5jjSALfrEAAAwAa9Oig8799.png)
Spire.XLS for C++組件說明
![Spire.XLS for <b class='flag-5'>C++</b>組件說明](https://file1.elecfans.com/web3/M00/05/E7/wKgZO2eFwUuAbuoQAAAbn_khf8A091.png)
AKI跨語言調(diào)用庫神助攻C/C++代碼遷移至HarmonyOS NEXT
同樣是函數(shù),在C和C++中有什么區(qū)別
MES系統(tǒng)的最佳實踐案例
云計算平臺的最佳實踐
RTOS開發(fā)最佳實踐
ModusToolbox 3.2在c代碼中包含c++代碼的正確步驟是什么?
C++中實現(xiàn)類似instanceof的方法
![<b class='flag-5'>C++</b>中實現(xiàn)類似instanceof的方法](https://file1.elecfans.com/web2/M00/FE/0C/wKgaomaYe1CAQ31QAAAnf0IkoSU605.png)
OpenCV圖像識別C++代碼
C/C++中兩種宏實現(xiàn)方式
![](https://file1.elecfans.com/web2/M00/CD/75/wKgaomYgmg2ADWcPAAFu55dKSPQ208.jpg)
C/C++代碼動態(tài)測試工具VectorCAST插樁功能演示#代碼動態(tài)測試 #C++
鴻蒙OS開發(fā)實例:【Native C++】
![鴻蒙OS開發(fā)實例:【Native <b class='flag-5'>C++</b>】](https://file1.elecfans.com/web2/M00/C8/31/wKgZomYZMTCAaDv3AAY5x13C324319.jpg)
為什么很少用C++開發(fā)單片機
![為什么很少用<b class='flag-5'>C++</b>開發(fā)單片機](https://file1.elecfans.com/web2/M00/C6/8F/wKgaomYBGYeAEfT2AAAsOmCJ4d8781.png)
評論