RM新时代网站-首页

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

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

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

C++項目常見的命名規(guī)范

lhl545545 ? 來源:C語言與CPP編程 ? 作者:C語言與CPP編程 ? 2022-09-27 09:12 ? 次閱讀

本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風(fēng)格、安全性、可維護性、可移植性、多線程、性能、正確性等角度全面介紹了現(xiàn)代C++項目的最佳實踐。本文是該系列的第二篇。

C++最佳實踐:

1. 工具

2. 代碼風(fēng)格(本文)

3.安全性

4.可維護性

5.可移植性及多線程

6.性能

7.正確性和腳本

代碼風(fēng)格

代碼風(fēng)格最重要的是一致性,其次是遵循C++程序員習(xí)慣的閱讀風(fēng)格。

C++允許任意長度的標(biāo)識符名稱,因此在命名時沒必要非要保持簡潔,建議使用描述性名稱,并在風(fēng)格上保持一致。

CamelCase(駝峰命名法)

snake_case(蛇形命名法)

這兩種是很常見的命名規(guī)范,snake_case的優(yōu)點是,在需要的時候可以適配拼寫檢查器。

建立代碼風(fēng)格指南

無論建立什么樣的代碼風(fēng)格指南,一定要實現(xiàn)指定期望風(fēng)格的.clang-format文件。雖然這對命名沒有幫助,但對于開源項目來說,保持一致的風(fēng)格尤為重要。

許多IDE、編輯器都支持內(nèi)置的clang-format,或者可以很方便的通過加載項安裝。

VSCode: Microsoft C/C++ extension for VS Code[2]

CLion: ClangFormat as alternative formatter?

VisualStudio: ClangFormat[3]

Resharper++: Using Clang-Format[4]

Vim

Format your C family code[5]

vim-autoformat[6]

XCode: ClangFormat-Xcode[7]

通用C++命名約定

類以大寫字母開頭: MyClass。

函數(shù)和變量以小寫字母開頭: myMethod。

常量全部大寫: const double PI=3.14159265358979323。

C++標(biāo)準(zhǔn)庫(以及其他著名C++庫,如Boost[8])使用以下指導(dǎo)原則:

宏使用大寫和下劃線: INT_MAX。

模板參數(shù)名使用駝峰命名法: InputIterator。

所有其他名稱都使用蛇形命名法: unordered_map。

區(qū)分私有對象數(shù)據(jù)

使用m_前綴命名私有數(shù)據(jù),以區(qū)別于公共數(shù)據(jù),m_代表“member(成員)”數(shù)據(jù)。

區(qū)分函數(shù)參數(shù)

最重要的是保持代碼庫的一致性,這是一種有助于保持一致性的方式。

使用t_前綴命名函數(shù)參數(shù),t_可以被認為是“the”,但其可以表示任意含義,關(guān)鍵是要將函數(shù)參數(shù)與作用域內(nèi)的其他變量區(qū)分開來,同時遵循一致的命名策略。

可以為團隊選擇任何前綴或后綴,下面是一個例子,提出了一個有爭議的建議,相關(guān)討論見issue #11[9]。

structSize
{
intwidth;
intheight;

Size(intt_width,intt_height):width(t_width),height(t_height){}
};

//Thisversionmightmakesenseforthreadsafetyorsomething,
//butmoretothepoint,sometimesweneedtohidedata,sometimeswedon't.
classPrivateSize
{
public:
intwidth()const{returnm_width;}
intheight()const{returnm_height;}
PrivateSize(intt_width,intt_height):m_width(t_width),m_height(t_height){}

private:
intm_width;
intm_height;
};

不要用下劃線(_)作為名字的開頭

_ 開頭的名字有可能與編譯器或標(biāo)準(zhǔn)庫的保留名發(fā)生沖突: What are the rules about using an underscore in a C++ identifier?[10]

良好代碼風(fēng)格示例

classMyClass
{
public:
MyClass(intt_data)
:m_data(t_data)
{
}

intgetData()const
{
returnm_data;
}

private:
intm_data;
};

使Out-of-Source-Directory構(gòu)建

確保構(gòu)建生成的文件存放在與源文件夾分離的輸出文件夾中。

使用nullptr

C++11引入了nullptr表示空指針,應(yīng)該用來代替0或NULL來指示空指針。

注釋

注釋塊應(yīng)該使用//,而不是/* */,使用//可以更容易的在調(diào)試時注釋掉代碼塊。

//thisfunctiondoessomething
intmyFunc()
{
}

要在調(diào)試期間注釋掉這個函數(shù)塊,可以這樣做:

/*
//thisfunctiondoessomething
intmyFunc()
{
}
*/

如果函數(shù)頭注釋使用/* */,這么做就會有沖突。

永遠不要在頭文件中使用using namespace

這會導(dǎo)致正在using的命名空間被強行拉入到包含頭文件的所有文件的命名空間中,從而造成命名空間污染,并可能在導(dǎo)致名稱沖突。在實現(xiàn)文件中using命名空間就足夠了。

Include保護符

頭文件必須包含名稱清晰的include保護符,從而避免同一頭文件被多次include的問題,并防止與其他項目的頭文件發(fā)生沖突。

#ifndefMYPROJECT_MYCLASS_HPP
#defineMYPROJECT_MYCLASS_HPP

namespaceMyProject{
classMyClass{
};
}

#endif

此外還可以考慮使用#pragma once指令,這是許多編譯器的準(zhǔn)標(biāo)準(zhǔn),內(nèi)容簡短,意圖明確。

代碼塊必須包含{}

省略{}可能會導(dǎo)致代碼語義錯誤。

//BadIdea
//Thiscompilesanddoeswhatyouwant,butcanleadtoconfusing
//errorsifmodificationaremadeinthefutureandcloseattention
//isnotpaid.
for(inti=0;i

保持每行代碼長度合理

//BadIdea
//hardtofollow
if(x&&y&&myFunctionThatReturnsBool()&&caseNumber3&&(15>12||212||2

許多項目和編碼標(biāo)準(zhǔn)都對此制定了軟規(guī)則,即每行字符應(yīng)該少于80或100個,這樣的代碼通常更容易閱讀,此外還可以把兩個文件并排顯示在一個屏幕上,不用小字體也能看到全部代碼。

使用""表示include本地文件

...<>表示include系統(tǒng)文件[11]。

//BadIdea.Requiresextra-Idirectivestothecompiler
//andgoesagainststandards.
#include
#include

//WorseIdea
//Requirespotentiallyevenmorespecific-Idirectivesand
//makescodemoredifficulttopackageanddistribute.
#include
#include


//GoodIdea
//Requiresnoextraparamsandnotifiestheuserthatthefile
//isalocalfile.
#include
#include"MyHeader.hpp"

初始化成員變量

...使用成員初始化列表。

對于POD類型,初始化列表的性能與手動初始化相同,但對于其他類型,有明顯的性能提升,見下文。

//BadIdea
classMyClass
{
public:
MyClass(intt_value)
{
m_value=t_value;
}

private:
intm_value;
};

//BadIdea
//Thisleadstoanadditionalconstructorcallform_myOtherClass
//beforetheassignment.
classMyClass
{
public:
MyClass(MyOtherClasst_myOtherClass)
{
m_myOtherClass=t_myOtherClass;
}

private:
MyOtherClassm_myOtherClass;
};

//GoodIdea
//Thereisnoperformancegainherebutthecodeiscleaner.
classMyClass
{
public:
MyClass(intt_value)
:m_value(t_value)
{
}

private:
intm_value;
};

//GoodIdea
//Thedefaultconstructorform_myOtherClassisnevercalledhere,so
//thereisaperformancegainifMyOtherClassisnotis_trivially_default_constructible.
classMyClass
{
public:
MyClass(MyOtherClasst_myOtherClass)
:m_myOtherClass(t_myOtherClass)
{
}

private:
MyOtherClassm_myOtherClass;
};

在C++11中,可以為每個成員初始化默認值(使用=或使用{})。

使用=設(shè)置默認值

//...//
private:
intm_value=0;//allowed
unsignedm_value_2=-1;//narrowingfromsignedtounsignedallowed
//...//

這樣可以確保不會出現(xiàn)構(gòu)造函數(shù)“忘記”初始化成員對象的情況。

用大括號初始化默認值

用大括號初始化不允許在編譯時截斷數(shù)據(jù)長度。

//BestIdea

//...//
private:
intm_value{0};//allowed
unsignedm_value_2{-1};//narrowingfromsignedtounsignednotallowed,leadstoacompiletimeerror
//...//

除非有明確的理由,否則優(yōu)先使用{}初始化,而不是=。

忘記初始化成員會導(dǎo)致未定義行為錯誤,而這些錯誤通常很難發(fā)現(xiàn)。

如果成員變量在初始化后不會更改,則將其標(biāo)記為const。

classMyClass
{
public:
MyClass(intt_value)
:m_value{t_value}
{
}

private:
constintm_value{0};
};

由于不能給const成員變量賦值,拷貝賦值操作可能對這樣的類沒有意義。

總是使用命名空間

幾乎沒有理由需要全局命名空間中聲明標(biāo)識符。相反,函數(shù)和類應(yīng)該存在于適當(dāng)命名的命名空間中,或者存在于命名空間里的類中。放在全局命名空間中的標(biāo)識符有可能與來自其他庫(主要是沒有命名空間的C庫)的標(biāo)識符發(fā)生沖突。

為標(biāo)準(zhǔn)庫特性使用正確的整數(shù)類型

標(biāo)準(zhǔn)庫通常使用std::size_t來處理與尺寸相關(guān)的內(nèi)容,size_t的大小由實現(xiàn)定義。

一般來說,使用auto可以避免大部分問題。

請確保使用正確的整數(shù)類型,并與C++標(biāo)準(zhǔn)庫保持一致,否則有可能在當(dāng)前使用的平臺上不會發(fā)出警告,但如果切換到其他平臺,可能會發(fā)出警告。

注意,在對無符號數(shù)執(zhí)行某些操作時,可能會導(dǎo)致整數(shù)下溢。例如:

std::vectorv1{2,3,4,5,6,7,8,9};
std::vectorv2{9,8,7,6,5,4,3,2,1};
constautos1=v1.size();
constautos2=v2.size();
constautodiff=s1-s2;//diffunderflowstoaverylargenumber

使用.hpp和.cpp作為文件擴展名

歸根結(jié)底,這是個人喜好問題,但是.hpp和.cpp已被各種編輯器和工具廣泛認可。因此,這是一個務(wù)實的選擇。具體來說,Visual Studio只自動識別.cpp和.cxx為C++文件,而Vim不一定會把.cc識別為C++文件。

某個特別大的項目(OpenStudio[12])使用.hpp和.cpp表示用戶生成的文件,而使用.hxx和.cxx表示工具生成的文件。兩者都能被很好的識別,并且區(qū)分開來有很大的幫助。

不要混用tab和空格

某些編輯器喜歡在默認情況下使用tab和空格的混合縮進,這使得沒有使用完全相同的tab縮進設(shè)置的人很難閱讀代碼。請配置好編輯器,確保不會發(fā)生這種情況。

不要將有副作用的代碼放在assert()中

assert(registerSomeThing());//makesurethatregisterSomeThing()returnstrue

上述代碼在debug模式下構(gòu)建時可以成功運行,但在進行release構(gòu)建時會被編譯器刪除,從而造成debug和release構(gòu)建的行為不一致,原因在于assert()是一個宏,它在release模式下展開為空。

不要害怕模板

模板可以幫助我們堅持DRY原則[13]。由于宏有不遵守命名空間等問題,因此能用模板的地方就不要用宏。

明智的使用操作符重載

運算符重載是為了支持表達性語法。比如讓兩個大數(shù)相加看起來像a + b,而不是a.add(b)。另一個常見的例子是std::string,通常使用string1 + string2連接兩個字符串。

但是,使用過多或錯誤的操作符重載很容易寫出可讀性不強的表達式。在重載操作符時,要遵循stackoverflow文章[14]中描述的三條基本規(guī)則。

具體來說,記住以下幾點:

處理資源時必須重載operator=(),參見下面Rule of Zero章節(jié)。

對于所有其他操作符,通常只有在需要在上下文中使用時才重載。典型的場景是用+連接事物,負號可以被認為是“真”或“假”的表達式,等等。

一定要注意操作符優(yōu)先級[15],盡量避免不直觀的結(jié)構(gòu)。

除非實現(xiàn)數(shù)字類型或遵循特定域中可識別的語法,否則不要重載~或%這樣的外部操作符。

永遠不要重載```operator,()```[16](逗號操作符)。

處理流時使用非成員函數(shù)operator>>()和operator<<()。例如,可以重載operator<<(std::ostream &, MyClass const &),從而允許將類“寫入”到一個流中,例如std::cout或std::fstream或std::stringstream,后者通常用于創(chuàng)建值的字符串表示。

這篇文章描述了更多需要重載的常見操作符: What are the basic rules and idioms for operator overloading?[17]。

更多關(guān)于自定義操作符實現(xiàn)細節(jié)的技巧可以參考: C++ Operator Overloading Guidelines[18]。

避免隱式轉(zhuǎn)換

單參數(shù)構(gòu)造函數(shù)

可以在編譯時應(yīng)用單參數(shù)構(gòu)造函數(shù)在類型之間自動轉(zhuǎn)換,比如像std::string(const char *),這樣的轉(zhuǎn)換很方便,但通常應(yīng)該避免,因為可能會增加額外的運行時開銷。

相反,可以將單參數(shù)構(gòu)造函數(shù)標(biāo)記為explicit,從而要求顯式調(diào)用。

轉(zhuǎn)換操作符

與單參數(shù)構(gòu)造函數(shù)類似,編譯器可以調(diào)用轉(zhuǎn)換操作符,同樣也會引入額外開銷,也應(yīng)該被標(biāo)記為explicit。

//badidea
structS{
operatorint(){
return2;
}
};

//goodidea
structS{
explicitoperatorint(){
return2;
}
};

考慮Rule of Zero

Rule of Zero規(guī)定,除非所構(gòu)造的類具有某種新的所有權(quán)形式,否則不提供編譯器可以提供的任何函數(shù)(拷貝構(gòu)造函數(shù)、拷貝賦值操作符、移動構(gòu)造函數(shù)、移動賦值操作符、析構(gòu)函數(shù))。

目標(biāo)是讓編譯器提供在添加更多成員變量時自動維護的最佳版本。

這篇文章介紹了這一原則的背景,并解釋了幾乎可以覆蓋所有情況的實現(xiàn)技術(shù): C++'s Rule of Zero[19]。

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

    關(guān)注

    3

    文章

    3309

    瀏覽量

    42471
  • C++
    C++
    +關(guān)注

    關(guān)注

    22

    文章

    2108

    瀏覽量

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

    關(guān)注

    30

    文章

    4779

    瀏覽量

    68521
  • 編輯器
    +關(guān)注

    關(guān)注

    1

    文章

    805

    瀏覽量

    31163

原文標(biāo)題:C++最佳實踐 | 2. 代碼風(fēng)格

文章出處:【微信號:C語言與CPP編程,微信公眾號:C語言與CPP編程】歡迎添加關(guān)注!文章轉(zhuǎn)載請注明出處。

收藏 人收藏

    評論

    相關(guān)推薦

    現(xiàn)代C++項目的最佳實踐

    本系列是開源書C++ Best Practises[1]的中文版,全書從工具、代碼風(fēng)格、安全性、可維護性、可移植性、多線程、性能、正確性等角度全面介紹了現(xiàn)代C++項目的最佳實踐。本文是該系列的第三篇。
    發(fā)表于 09-29 11:32 ?1138次閱讀

    焊盤命名規(guī)范

    焊盤命名規(guī)范 通常我們的焊盤分為通過孔(THP)焊盤和表貼(SMD)焊盤兩種形式。但這兩種形式當(dāng)中,又有多種形狀。所以我們要有一個統(tǒng)一的命名規(guī)范,以方便以后調(diào)用。一、THP焊盤
    發(fā)表于 12-31 17:27

    C語言常見命名規(guī)則

    C語言常見命名規(guī)則
    發(fā)表于 08-17 14:44

    關(guān)于Android命名規(guī)范

    ”。接手別人的項目,如果對方代碼規(guī)范,是好事,可以學(xué)學(xué)人家如何寫的,如果代碼隨意,那就蛋疼了,像我這種強迫癥患者,一刻都不能忍,分分鐘就想重構(gòu)!下面列舉幾個我接盤項目遺留的坑:包名包名全部小寫編寫,連續(xù)
    發(fā)表于 10-15 00:11

    關(guān)于Android命名規(guī)范

    ”。接手別人的項目,如果對方代碼規(guī)范,是好事,可以學(xué)學(xué)人家如何寫的,如果代碼隨意,那就蛋疼了,像我這種強迫癥患者,一刻都不能忍,分分鐘就想重構(gòu)!下面列舉幾個我接盤項目遺留的坑:包名 包名全部小寫編寫,連續(xù)
    發(fā)表于 06-21 09:35

    嵌入式開發(fā)程序的架構(gòu)和命名規(guī)范

    c/c++程序風(fēng)格約定文/Mike往往我們做單片機程序開發(fā)的時候,很少去注意程序的架構(gòu)和命名規(guī)范,然而只要是涉及到代碼開發(fā),這都是不可回避的問題,很多小公司做嵌入式的開發(fā),在技術(shù)上沒有
    發(fā)表于 11-05 08:22

    使用C++項目的正確方法是什么?

    在 STM32CubeIDE 中,我創(chuàng)建了一個 C++ 項目。但是 `main.c` 仍然以 .c 擴展名生成,并使用 C 編譯器編譯。這就
    發(fā)表于 02-07 08:34

    C++命名空間的幾大用法

    的: 命名空間定義了新的作用域。它們提供了一種避免名稱沖突的方法。 c++ 中的命名空間通常用于避免命名沖突。盡管命名空間在最近的
    發(fā)表于 09-28 18:31 ?0次下載

    CC++的編程規(guī)范資料免費下載

    本文檔的主要內(nèi)容詳細介紹的是CC++的編程規(guī)范資料免費下載包括了:1. 文件結(jié)構(gòu),2. 程序版式,3 命名規(guī)則,4. 表達式和基本語句,5. 常量,6. 函數(shù)設(shè)計,7 重載和內(nèi)聯(lián),8
    發(fā)表于 04-08 08:00 ?5次下載
    <b class='flag-5'>C</b>和<b class='flag-5'>C++</b>的編程<b class='flag-5'>規(guī)范</b>資料免費下載

    C++語言編碼規(guī)范詳細說明

    本文檔的主要內(nèi)容詳細介紹的是C++語言編碼規(guī)范詳細說明。
    發(fā)表于 01-07 16:19 ?14次下載
    <b class='flag-5'>C++</b>語言編碼<b class='flag-5'>規(guī)范</b>詳細說明

    一文了解C++命名空間

    C++中,變量、函數(shù)和類都是大量存在的,這些變量、函數(shù)和類的名稱將都存在于全局命名空間中,會導(dǎo)致很多沖突, 使用命名空間的目的是對標(biāo)識符的名稱進行本地化,以避免命名沖突或名字污染,N
    的頭像 發(fā)表于 06-29 14:48 ?2325次閱讀
    一文了解<b class='flag-5'>C++</b>的<b class='flag-5'>命名</b>空間

    關(guān)于軟件、硬件版本號命名常見規(guī)范

    不知道大家寫程序、畫板子時,版本號是怎么命名的? ? 最常見的就是V1.0.0這種簡單的形式命名,復(fù)雜一點就是帶有日期、后綴等版本信息。 ? 當(dāng)然,版本號命名
    的頭像 發(fā)表于 12-31 10:04 ?4768次閱讀

    C++語言里有哪些比較常見的坑?

    ? 前段時間給部門做了個C++專題的分享,主要分享了C++語言里一些常見的坑,在這里也分享給大家。 ? 首先說下C++C語言有什么區(qū)別?分
    的頭像 發(fā)表于 05-20 11:38 ?2087次閱讀

    C++常見設(shè)計模式解析與實現(xiàn)

    C++常見設(shè)計模式解析與實現(xiàn)說明。
    發(fā)表于 06-01 15:44 ?11次下載

    c++知識要點

    第一章 開始 1.1 編寫一個簡單的C++程序 int main() { return 0; } 每個C++程序都包含一個或多個函數(shù),其中一個必須命名為main.
    發(fā)表于 06-20 09:45 ?0次下載
    RM新时代网站-首页