C++ 編碼規範小結
前言
最近體會到真實的一句話吧,年輕人終歸是年輕人,菜就是菜啊,最近我感覺我真的好菜啊。
最近寫代碼,學習,經歷了一些事情,理解到編碼規範、Review的重要性,瞭解到建立一套自己的一些編碼行爲習慣是一件對於程序員來說較爲重要的行爲習慣,越早建立,對於未來的約束和發展越好,因此,在學習了一部分編碼習慣後,在此提供一套自己的編碼習慣作爲給各位的參考,大家可以按需對部分的習慣進行修改,不必拘泥於我的習慣,個人提供僅供參考,與諸君共勉。
1. 命名約定
- 類名、函數名、項目名、文件名等 不使用縮寫名字。但專用的名字則使用縮寫,如Http,Cpu,Dvd,Gui,Guid等,且除第一個字母大寫外,其餘字母小寫:
1. void exportHtmlSource()
- 類名、typedef、模板類必須大寫開頭:
1. class FrameVecotr;
2. typedef unsigned long BitMask;
3. template<typename T>
class MyClass {};
- 常量和宏必須全部大寫,並使用下劃線隔開單詞:
1. const int ITERATIONS = 25;
2. #define RADIUS 30;
- 變量名必須小寫開頭:
int lineWidth
對於枚舉類型的值
- 推薦使用強枚舉類型的值,比如:
1. enum class Color { Blue, Orange }; Color c = Color::Blue;
- 非強類型枚舉值必須全部大寫,以枚舉名爲前綴,並使用下劃線隔開單詞,比如:
enum { Insensitive, Sensitive };
// 要改爲
enum { CASE_INSENSITIVE, CASE_SENSITIVE };
- 模板推薦使用typename關鍵字。如果類型一定不是基礎類型,則可使用class:
template<typename T> VS template<class PixelType>
- 類成員變量使用m_前綴:
std::string m_name;
bool m_visible;
類的getter成員函數命名規則
- 形容詞以is-爲前綴,比如:
isEmpty(), isChecked();
- 修飾名詞的形容詞沒有前綴,比如:
// 使用
scrollBarsEnabled();
// 而不是
areScrollBarsEnabled();
- 動詞+名詞時,動詞沒有is-前綴,也不使用第三人稱(-s),比如:
// 使用
acceptDrops();
// 而不是
isAcceptsDrops();
- 考慮到自己寫的學習項目大部分代碼會使用Qt,還是保持使用Qt的風格,名詞不使用get-爲前綴,比如:
width();
name();
autoCompletion();
- 名詞不帶前綴容易混淆時,考慮加上is-或has-前綴,比如:
// 使用
isDialog();
// 而不是
dialog();
// dialog()讓人誤以爲返回個對話框對象。
// 使用hasFocus(),而不是focus(),focus()讓人誤以爲返回個焦點對象。
- 類的setter成員函數命名規則:名稱來源於getter,只是去掉了is-等前綴,在前面加上了set,例如:
setEnabled();
setScrollBarsEnabled();
setFocus();
- 平時寫GUI控件這類代碼的時候,相關的變量名推薦以控件類型爲後綴
void* mainWindow;
void* mainForm;
void* fileMenu;
void* exitButton;
- 推薦列表容器對象的後綴使用List(單向列表)或Array(雙向列表):
std::list vertexList;
std::array pointArray;
- 表示數據的變量使用n爲前綴:
int nPoints;
int nLines;
- 避免使用否定的bool變量:
bool isFound;
// 而不是
bool isNotFound;
- 異常的子類加上Error或Exception後綴,如:
class AccessError;
- 推薦使用對稱性單詞作爲函數名,如下:
add/remove, create/destroy, start/stop, insert/delete, increment/decrement,
old/new, begin/end, first/last, up/down, min/max, next/previous, open/close,
show/hide, suspend/resume
- 優先使用單詞"make"而不是create/build/compute來給創建函數命名。但如果有使用習慣,按慣例命名,例如在工廠設計模式裏,使用"create"命名。
2. 文件
-
優先使用.h作爲頭文件擴展名,如果頭文件裏有模板,將使用.hpp作爲頭文件擴展名。如果需要定義較多的內聯函數,可將內聯函數放到.inl文件裏。
-
源文件擴展名必須是.cpp。
-
通常頭文件裏只定義一個類,但如果相關的類比較小,也可以在同個文件裏定義。
-
文件名使用項目名+文件裏的主要類的名字,所有單詞爲小寫且使用下劃線連接,比如gui_base_combo_box.h和gui_base_combo_box.cpp。(純個人習慣)
-
使用#define保護頭文件,而不是#pragma once。格式如下:H_H,例如#define WIDGETS_COMBO_BOX_H_H
-
前置聲明:
- 儘量避免前置聲明那些定義在其他項目中的類型。
- 函數不使用前置聲明,而是使用#include。
- 類模板優先使用#include。
-
#include
- 使用#include “”引用解決方案裏的頭文件,使用#include <>引用系統、標準庫和第三方庫頭文件。
- 引用的頭文件路徑使用”/”而不是”\”,不能使用”.”或”…”相對路徑:#include <boost/any>
- 由於文件名已經帶了項目的名字,不存在文件名重複的情況,大部分情況下不需要使用全路徑。但如果引用項目內的子模塊的頭文件,則仍然需要使用全路徑。
-
#include頭文件使用分組方式,使用空行隔開,爲了及時發現項目內部錯誤,引用順序如下:
- 源文件所對應的頭文件
- 本項目的頭文件
- 解決方案裏的其它項目的頭文件
- C系統頭文件
- C++系統頭文件
- 引用的第三方庫頭文件
// 例如:
#include "foo/public/fooserver.h" // 優先位置
#include "foo/public/bar.h"
//空一行
#include "base/basictypes.h"
#include "base/commandlineflags.h"
//空一行
#include <sys/types.h>
#include <unistd.h>
//空一行
#include <hash_map>
#include <vector>
//空一行
#include <boost/any>
- 頭文件和源文件頂部都需要增加版權聲明,如下:
3. 項目
- 必須有一個<項目名>.h或<項目名>_global.h的頭文件,用於顯明該項目內部的全局接口,該頭文件也用於預編譯頭文件。如果項目有對外公開的接口,需要創建<項目名>_export.h頭文件,用於聲明導出。
- 項目有全局的私有接口,則項目需要有一個<項目名字>_private.h的頭文件,私有接口不使用__declspec(dllexport)等聲明進行導出。私有接口自己在項目內部使用即可。
4. 類
- 類名使用Zs前綴。(Zs代表 Zty Soft)(純個人習慣)
- 所有的對外SDK接口類都定義在ZsBase項目裏,所以SDK的接口類命名爲Zs++Base。非SDK的接口類也延續使用這個命名方式。
- 單參數的構造函數使用explicit,防止隱式構造。C++ 11 explicit構造函數可以禁止初始化列表構造,例如:
class Foo
{
explicit Foo(int x);
explicit Foo(int x, double y);
};
void Func(Foo f);
- 構造函數裏禁止調用虛函數,因爲此時函數無法實現多態。
- 類有虛函數時必須把析構函數聲明爲虛函數。
- 在未明確虛函數的作用時儘量少定義虛函數。
- 所有繼承都應該是public的,否則使用成員變量的方式實現。
- 慎用多繼承。但如果用多繼承,除第一個父類外,其它父類都必須是純接口類。
- 除了static const類型成員外,成員變量推薦使用私有權限,不要過度使用protected。
- 多用override和final關鍵字來識別虛函數,包括虛析構函數。
- 如果定義了複製構造函數和賦值函數,則需考慮是否也定義移動構造函數和移動賦值函數(C++ 11的移動語句)。
5. 函數
- 內聯函數的代碼不超過10行。如果內聯函數的代碼不超過5行可以在類內定義,否則在類外定義。
- 函數重載:
- 重載不是由參數個數決定,否則可使用std::vector或初始化列表實現
- 重載不是由參數類型決定,解決參數類型重載可以使用函數名+類型的方式實現,比如appendInt(int), appendDouble(double)等。
- 默認參數:
- 只允許在非虛函數使用默認參數。
- 儘可能不使用默認參數,因爲默認參數可以通過函數重載實現。在不產生歧義的情況下,才使用默認參數,有任何疑惑就使用重載。
- lambda函數:
- 雖然編譯器能自動識別lambda的返回值,但建議在lambda表達式寫上後置返回值。但比較複雜的返回值時可以不寫。
- 不使用默認捕捉(即[=],[&]),要列出具體捕捉對象。
- 函數參數要直觀,讓人易於理解。比如:
QSlider *slider = new QSlider(12, 18, 3, 13, Qt::Vertical, 0, "volume");
//參數代表什麼?這句代碼不如以下直觀
QSlider *slider = new QSlider(Qt::Vertical);
slider->setRange(12, 18);
slider->setPageStep(3);
slider->setValue(13);
slider->setObjectName("volume");
- lambda函數
- 建議不使用bool參數來控制函數執行,容易讓人疑惑。比如:
widget->repaint(false); //false是指不重繪嗎?
// 實際上可以分爲以下兩個函數,或者使用枚舉值來代替bool
widget->repaint();
widget->repaintWithoutErasing();
- 函數參數:
- 推薦傳引用參數,少傳指針,引用可以避免空指針和指針偏移操作。
- 輸入的引用或指針參數使用const修飾。比如:bool QWidget::isVisibleTo(const QWidget *ancestor) const;
- 輸入參數類型大小小於16個字節並且是POD類型,使用傳值方式。比如:setWidth(int w);沒必要寫成setWidth(const int& w);
- 輸出參數使用指針而非引用,因爲使用指針可使用戶代碼可讀性更好。比如:getWidth(&w)比getWidth(w)更能說明參數是輸出的。
- 有多個輸出參數時,考慮將參數做爲簡單的結構輸出。比如color.getHsv(&h, &s, &v);可改成:struct Hsv { int h, s ,v }; Hsv v = color.getHsv();
- getter等不會改變類成員變量的函數聲明爲const。比如:bool QWidget::isVisible() const;
// 推薦傳引用參數,少傳指針,引用可以避免空指針和指針偏移操作。
// 輸入的引用或指針參數使用const修飾。比如:
bool QWidget::isVisibleTo(const QWidget *ancestor) const;
// 輸入參數類型大小小於16個字節並且是POD類型,使用傳值方式。比如:
setWidth(int w);
// 沒必要寫成
setWidth(const int& w);
// 輸出參數使用指針而非引用,因爲使用指針可使用戶代碼可讀性更好。比如:
getWidth(&w);getWidth(w);
//比getWidth(w)更能說明參數是輸出的。
// 有多個輸出參數時,考慮將參數做爲簡單的結構輸出。比如
color.getHsv(&h, &s, &v);
// 可改成:
struct Hsv { int h, s ,v }; Hsv v = color.getHsv();
// getter等不會改變類成員變量的函數聲明爲const。比如:
bool QWidget::isVisible() const;
6. 命名空間
- 使用ZS_BEGIN_NAMESPACE和ZS_END_NAMESPACE兩個宏進行聲明命名空間。通常類名已能區分不同的實體,但爲了防止少見的名字衝突才引入命名空間。
- 禁止使用using引入命名空間,using namespace XXX會污染命名空間。
- 禁止在頭文件裏使用命名空間別名,namespace baz = ::foo::bar::baz;
- 在源文件裏可以使用匿名命名空間,但頭文件裏禁止使用匿名命名空間。
- 禁止使用std作爲項目的命名空間。
7. 其他約定(雜記)
- GUI項目羣裏使用4個空格的縮進方式,不要使用tab。可通過VS Tools/Options/Text Editor/C/C++/Tabs進行設置。
- 右值引用:除在移動構造函數和移動賦值操作函數裏使用右值引用,其它代碼都不要使用,原因是容易誤用。可以使用std::move,但不要使用std::forward。
- 慎重使用無符號整數,因爲整數強轉爲無符號時,往往會帶來很多bug。
- 代碼註釋必須使用英文,GIT提交時的註釋推薦用英文(但不強求)。
- 打開VS的空格可見性設置,在寫代碼時才能注意到有沒有使用tab縮進。VS菜單Edit/Advanced/View White Space
設置縮進的方法:
設置顯示空格的方法:
成長,就是一個不動聲色的過程,一個人熬過一些苦,才能無所不能。