0
  • 聊天消息
  • 系統(tǒng)消息
  • 評論與回復(fù)
登錄后你可以
  • 下載海量資料
  • 學(xué)習(xí)在線課程
  • 觀看技術(shù)視頻
  • 寫文章/發(fā)帖/加入社區(qū)
會員中心
創(chuàng)作中心

完善資料讓更多小伙伴認(rèn)識你,還能領(lǐng)取20積分哦,立即完善>

3天內(nèi)不再提示

C++代碼需要遵循的10個最佳實(shí)踐

CPP開發(fā)者 ? 來源:DeepNoMind ? 作者:俞凡 ? 2022-10-18 15:20 ? 次閱讀

C++代碼提供了足夠的靈活性,因此對于大部分工程師來說都很難把握。本文介紹了寫好C++代碼需要遵循的10個最佳實(shí)踐,并在最后提供了一個工具可以幫助我們分析C++代碼的健壯度。

1. 盡可能嘗試使用新的C++標(biāo)準(zhǔn)

到2022年,C++已經(jīng)走過了40多個年頭。新的C++標(biāo)準(zhǔn)實(shí)際上簡化了許多令人沮喪的細(xì)節(jié),提供了新的現(xiàn)代方法來改進(jìn)C++代碼,但讓開發(fā)人員認(rèn)識到這一點(diǎn)并不容易。

以內(nèi)存管理為例,這可能是C++中受到最多批評的機(jī)制。多年來,對象分配都是由new關(guān)鍵字完成的,開發(fā)人員一定得記住在代碼的某個地方調(diào)用delete。“現(xiàn)代C++”解決了這個問題,并促進(jìn)了共享指針的使用。

2. 使用命名空間模塊化代碼

現(xiàn)代C++庫廣泛使用命名空間來模塊化代碼庫,它們利用“Namespace-by-feature”方法,按功能劃分命名空間來反映功能集,將單個特性(且僅與該特性)相關(guān)的所有內(nèi)容放到單個命名空間中。從而使得命名空間具有高內(nèi)聚性和高模塊化,并且耦合最小,緊耦合的項目被放在了一起。

Boost是按特性分組的最佳示例,其包含數(shù)千個命名空間,每個命名空間用于對特定的特性進(jìn)行分組。

3. 抽象

數(shù)據(jù)抽象是C++中面向?qū)ο?a target="_blank">編程最基本和最重要的特性之一。抽象意味著只顯示基本信息而隱藏細(xì)節(jié),數(shù)據(jù)抽象指的是僅向外部世界提供關(guān)于數(shù)據(jù)的基本信息,隱藏背景細(xì)節(jié)或?qū)崿F(xiàn)。

盡管許多書籍、網(wǎng)絡(luò)資源、會議演講者和專家都推薦這種最佳實(shí)踐,但在很多項目中,這條規(guī)則仍然被忽略了,許多類的細(xì)節(jié)并沒有被隱藏。

4. 類越小越好

具有多行代碼的類型應(yīng)該被劃分為一組較小的類型。

需要很大的耐心重構(gòu)一個大的類,甚至可能需要從頭重新創(chuàng)建所有東西。以下是一些重構(gòu)建議:

BigClass中的邏輯必須被分成更小的類。這些較小的類最終可能成為嵌套在原始God Class中的私有類,God Class的實(shí)例對象由較小嵌套類的實(shí)例組成。

較小的類劃分應(yīng)該由God Class負(fù)責(zé)的多個職責(zé)驅(qū)動。要確定這些職責(zé),通常需要查找與字段的子集強(qiáng)耦合的方法的子集。

如果BigClass包含的邏輯比狀態(tài)多,一個好的選擇是定義一個或幾個不包含靜態(tài)字段而只包含純靜態(tài)方法的靜態(tài)類。純靜態(tài)方法是一種只根據(jù)輸入參數(shù)計算結(jié)果的函數(shù),它不讀取或分配任何靜態(tài)或?qū)嵗侄?。純靜態(tài)方法的主要優(yōu)點(diǎn)是易于測試。

首先嘗試維護(hù)BigClass的接口,并委托調(diào)用新提取的類。最后,BigClass應(yīng)該是一個沒有自己邏輯的純接口,可以為了方便將其保留,也可以將其扔掉,并開始只使用新類。

單元測試可以提供幫助: 在提取方法之前為每個方法編寫測試,以確保不會破壞功能。

5. 每個類盡量提供最少的方法

包含20個以上方法的類可能很難理解和維護(hù)。

一個類有許多方法可能是實(shí)現(xiàn)了太多責(zé)任的癥狀。

也許所面對的類控制了系統(tǒng)中太多的其他類,并且已經(jīng)超出了應(yīng)有的邏輯,成為了一個無所不能的類。

6. 加強(qiáng)低耦合

低耦合是理想狀態(tài),可以在應(yīng)用中進(jìn)行較少的更改實(shí)現(xiàn)程序的某個變更。從長遠(yuǎn)來看,可以減少修改、添加新特性的大量時間、精力和成本。

低耦合可以通過使用抽象類或泛型類和方法來實(shí)現(xiàn)。

7. 加強(qiáng)高內(nèi)聚

單一責(zé)任原則規(guī)定一個類不應(yīng)該有多于一個更改的理由,這樣的類被稱為內(nèi)聚類。較高的LCOM值通??梢砸馕吨惖膬?nèi)聚性較差。有幾個LCOM指標(biāo),取值范圍為[0-1]。LCOM HS (HS代表Henderson-Sellers)取值范圍為[0-2]。LCOM HS值大于1時需要產(chǎn)生警惕。下面是計算LCOM指標(biāo):

LCOM = 1 — (sum(MF)/M*F)
LCOM HS = (M — sum(MF)/F)(M-1)

其中……

M是類中方法的數(shù)量(包括靜態(tài)方法和實(shí)例方法,它還包括構(gòu)造函數(shù)、屬性getter/setter、事件添加/刪除方法)。

F是類中實(shí)例字段的數(shù)量。

MF是類訪問特定實(shí)例字段的方法數(shù)量。

Sum(MF)是該類所有實(shí)例字段的MF之和。

這些公式背后的基本思想可以表述如下: 如果一個類的所有方法都使用它的所有實(shí)例字段,那么這個類就是完全內(nèi)聚的,這意味著sum(MF)=M*F,然后LCOM = 0和LCOMHS = 0。

LCOMHS值大于1就需要警惕了。

8. 只注釋代碼不能表達(dá)的內(nèi)容

鸚鵡學(xué)舌的代碼注釋沒有為讀者提供任何額外的東西。代碼庫中充斥著嘈雜的注釋和不正確的注釋,促使程序員忽略所有的注釋,或者采取積極的措施隱藏它們。

9. 盡量不要用重復(fù)的代碼

眾所周知,重復(fù)代碼的存在對軟件開發(fā)和維護(hù)有負(fù)面影響。實(shí)際上,一個主要缺點(diǎn)是,當(dāng)為了修復(fù)bug或添加新特性而更改重復(fù)代碼的實(shí)例時,所有對應(yīng)的代碼必須同時更改。

產(chǎn)生重復(fù)代碼最常見的原因是復(fù)制/粘貼操作,這種情況下,相似的源代碼出現(xiàn)在兩個或多個地方。許多文章、書籍和網(wǎng)站都警告不要采用這種做法,但有時實(shí)踐這些建議并不容易,開發(fā)人員還是會選擇簡單的解決方案: 復(fù)制/粘貼大法。

使用適當(dāng)?shù)墓ぞ呖梢匀菀椎膹膹?fù)制/粘貼操作中檢測到重復(fù)代碼,但是,在某些情況下,克隆代碼很難被檢測到。

10. 不變性有助于多線程編程

基本上,如果對象在創(chuàng)建之后狀態(tài)不變,那么這個對象就是不可變(immutable)的。如果一個類的實(shí)例是不可變的,那么該類就是不可變的。

不可變對象極大簡化了并發(fā)編程,這是支持使用它的重要理由。想想看,為什么編寫適當(dāng)?shù)亩嗑€程程序是一項艱巨的任務(wù)?因?yàn)橥骄€程訪問資源(對象或其他操作系統(tǒng)資源)是很困難的。為什么同步這些訪問很困難?因?yàn)楹茈y保證多個線程對多個對象進(jìn)行的多次寫訪問和讀訪問之間不會出現(xiàn)競爭條件。如果不再有寫訪問會怎么樣?換句話說,如果被線程訪問的對象的狀態(tài)沒有改變會怎么樣?就不再需要同步了!

關(guān)于不可變類的另一個好處是它們永遠(yuǎn)不會違反里氏替換原則(LSP, Liskov Subtitution Principle),以下是維基百科對LSP的定義:

Liskov的行為子類型的概念定義了可變對象可替換性的概念,也就是說,如果S是T的子類型,那么程序中T類型的對象可以被替換為S類型的對象,而不改變該程序的任何期望屬性(例如,正確性)。

如果沒有公共字段,沒有可以更改其內(nèi)部數(shù)據(jù)的方法,并且派生類方法無法更改其內(nèi)部數(shù)據(jù),那么引用對象類就是不可變的。因?yàn)橹挡豢勺?,所以在所有情況下都可以引用相同的對象,不需要復(fù)制構(gòu)造函數(shù)或賦值操作符。出于這個原因,建議將復(fù)制構(gòu)造函數(shù)和賦值操作符設(shè)為私有,或者從boost::noncopyable繼承,或者使用新的C++ 11特性“顯式默認(rèn)和刪除特殊成員函數(shù)”[2]。

如何加強(qiáng)對這些最佳實(shí)踐進(jìn)行檢查?

CppDepend[3]提供了名為CQLinq[4]的代碼查詢語言,可以像數(shù)據(jù)庫一樣查詢代碼庫。開發(fā)人員、設(shè)計人員和架構(gòu)師可以自定義查詢,以便輕松找到容易出現(xiàn)bug的情況。

通過CQLinq,可以結(jié)合來自代碼度量、依賴關(guān)系、API使用和其他模型的數(shù)據(jù)來定義非常高級的查詢,以匹配容易出現(xiàn)bug的情況。

例如,分析clang源代碼后,可以檢測到大類:

873f83c0-4e99-11ed-a3b6-dac502259ad0.png

檢測到有大量方法的類:

876b6bac-4e99-11ed-a3b6-dac502259ad0.png

或者檢測到內(nèi)聚性較差的類:

8802c402-4e99-11ed-a3b6-dac502259ad0.png

審核編輯:郭婷


聲明:本文內(nèi)容及配圖由入駐作者撰寫或者入駐合作網(wǎng)站授權(quán)轉(zhuǎn)載。文章觀點(diǎn)僅代表作者本人,不代表電子發(fā)燒友網(wǎng)立場。文章及其配圖僅供工程師學(xué)習(xí)之用,如有內(nèi)容侵權(quán)或者其他違規(guī)問題,請聯(lián)系本站處理。 舉報投訴
  • C++
    C++
    +關(guān)注

    關(guān)注

    21

    文章

    2085

    瀏覽量

    73301
  • 代碼
    +關(guān)注

    關(guān)注

    30

    文章

    4670

    瀏覽量

    67761
收藏 人收藏

    評論

    相關(guān)推薦

    ModusToolbox 3.2在c代碼中包含c++代碼的正確步驟是什么?

    使用 ModusToolbox 3.2 我有一用純 C 語言編寫的 XMC4700 項目。 我正在嘗試添加一些 C++ 函數(shù),并將其合并到我的原始代碼中。 我可以構(gòu)建獨(dú)立的 .cpp
    發(fā)表于 07-23 08:21

    OpenCV圖像識別C++代碼

    的頭文件 在您的C++代碼中,包含以下必要的頭文件: # include # include # include # include # include # include # include 讀取圖像
    的頭像 發(fā)表于 07-16 10:42 ?1082次閱讀

    C/C++中兩種宏實(shí)現(xiàn)方式

    #ifndef的方式受C/C++語言標(biāo)準(zhǔn)支持。它不僅可以保證同一文件不會被包含多次,也能保證內(nèi)容完全相同的兩文件(或者代碼片段)不會被不
    的頭像 發(fā)表于 04-19 11:50 ?432次閱讀

    C/C++代碼動態(tài)測試工具VectorCAST插樁功能演示#代碼動態(tài)測試 #C++

    C++代碼
    北匯信息POLELINK
    發(fā)布于 :2024年04月18日 11:57:45

    使用aurix c/c++任務(wù)調(diào)試器調(diào)試代碼,為什么無法得到結(jié)果?

    我正在嘗試使用 aurix 開發(fā)工作室 c/c++ 任務(wù)調(diào)試器調(diào)試代碼,但無法得到結(jié)果。 CAN 你幫我了解需要安裝哪些軟件以及需要
    發(fā)表于 01-30 06:24

    C++簡史:C++是如何開始的

    MISRA C++:2023,MISRA? C++ 標(biāo)準(zhǔn)的下一版本,來了!為了幫助您做好準(zhǔn)備,我們介紹了 Perforce 首席技術(shù)支持工程師 Frank van den Beuken 博士撰寫
    的頭像 發(fā)表于 01-11 09:00 ?424次閱讀
    <b class='flag-5'>C++</b>簡史:<b class='flag-5'>C++</b>是如何開始的

    TorchFix:基于PyTorch的代碼靜態(tài)分析

    TorchFix是我們最近開發(fā)的一新工具,旨在幫助PyTorch用戶維護(hù)健康的代碼庫并遵循PyTorch的最佳實(shí)踐。首先,我想要展示一些我
    的頭像 發(fā)表于 12-18 15:20 ?944次閱讀

    C語言和C++中那些不同的地方

    C語言雖說經(jīng)常和C++在一起被大家提起,但可千萬不要以為它們是一東西?,F(xiàn)在我們常用的C語言是C89標(biāo)準(zhǔn),
    的頭像 發(fā)表于 12-07 14:29 ?772次閱讀
    <b class='flag-5'>C</b>語言和<b class='flag-5'>C++</b>中那些不同的地方

    如何選擇創(chuàng)建c語言和c++

    選擇創(chuàng)建 C 語言和 C++需要綜合考慮多個因素。在決定使用哪種語言之前,我們需要對這兩種語言的特點(diǎn)、優(yōu)缺點(diǎn)、適用場景、學(xué)習(xí)成本等進(jìn)行全面的了解和對比。下面是關(guān)于選擇創(chuàng)建
    的頭像 發(fā)表于 11-27 15:58 ?455次閱讀

    c++怎么開始編程

    應(yīng)用程序、嵌入式系統(tǒng)和網(wǎng)絡(luò)應(yīng)用程序等各種領(lǐng)域。 在開始編程之前,你需要安裝C++的編程環(huán)境。首先,你需要下載并安裝一編譯器,比如微軟的Visual Studio、GNU的GCC或者C
    的頭像 發(fā)表于 11-27 15:56 ?731次閱讀

    c++多行注釋快捷鍵

    */ 結(jié)束。在這兩標(biāo)記之間的所有內(nèi)容都會被視為注釋,并且不會參與編譯和執(zhí)行。 為了添加或刪除多行注釋,你可以使用C++的集成開發(fā)環(huán)境(IDE)提供的快捷鍵。下面是一些常見的C++開發(fā)環(huán)境中常用的多行注釋快捷鍵: Visual
    的頭像 發(fā)表于 11-22 10:24 ?6761次閱讀

    開發(fā)微服務(wù)的9最佳實(shí)踐

    單一職責(zé)原則是 OOP 中的任何單個對象都應(yīng)該針對一特定功能而創(chuàng)建的概念?;旧?,它是羅伯特·馬丁提出的編程原則的一部分。就像代碼一樣,一類應(yīng)該只有一
    的頭像 發(fā)表于 11-09 16:56 ?310次閱讀
    開發(fā)微服務(wù)的9<b class='flag-5'>個</b><b class='flag-5'>最佳</b><b class='flag-5'>實(shí)踐</b>

    C++異步日志實(shí)踐

    高效可拓展的異步C++日志庫:RING LOG,本文分享了了其設(shè)計方案與技術(shù)原理等內(nèi)容 導(dǎo)論 同步日志與缺點(diǎn) 傳統(tǒng)的日志也叫同步日志,每次調(diào)用一次打印日志API就對應(yīng)一次系統(tǒng)調(diào)用write寫日志
    的頭像 發(fā)表于 11-09 10:29 ?529次閱讀
    <b class='flag-5'>C++</b>異步日志<b class='flag-5'>實(shí)踐</b>

    編程語言中一奇怪的代碼結(jié)構(gòu)

    C語言和C++等編程語言中,我們常常會遇到一奇怪的代碼結(jié)構(gòu)。
    發(fā)表于 11-01 10:24 ?314次閱讀
    編程語言中一<b class='flag-5'>個</b>奇怪的<b class='flag-5'>代碼</b>結(jié)構(gòu)

    C++之父新作帶你勾勒現(xiàn)代C++地圖

    為了幫助大家解決這些痛點(diǎn)問題,讓大家領(lǐng)略現(xiàn)代C++之美,掌握其中的精髓,更好地使用C++,C++之父Bjarne Stroustrup坐不住了,他親自操刀寫就了這本《C++之旅》!
    的頭像 發(fā)表于 10-30 16:35 ?696次閱讀
    <b class='flag-5'>C++</b>之父新作帶你勾勒現(xiàn)代<b class='flag-5'>C++</b>地圖