C++编程规范

背景

C++是很多项目的主要开发语言。项目一般是多人开发,而每个人的编码风格一般各有不同。如果没有一套C++的编程规范,项目内的代码将难以阅读与维护。

因此此文档将对C++的编程风格作一些约定,以便项目内的开发者可以写出风格一致的代码。

下面将围绕以下思维导图展开讨论C++编程规范的细节

c_style_mind

头文件

自包含头文件

头文件应该能够自给自足,例如头文件 foo.h 可以置于 foo.cpp 文件的第一行

换句话说,头文件包含了它所需要的其他头文件,当别的文件引用此头文件的时候,不需要额外引用其他头文件才能够使用。

#define保护

头文件应该使用#define保护,防止头文件被多重包含。

按以下方式保护:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_

内联函数

只有当函数在10行以内才将其定义为内联函数。

内联函数一般不能包含循环、switch语句、递归或者是虚函数。

#include的路径及顺序

例如dir/foo.cpp包含的头文件的次序如下:

  1. dir/foo.h
  2. C 系统文件
  3. C++ 系统文件
  4. 其他库的 .h 文件
  5. 本项目内 .h 文件

作用域

命名空间

  • 鼓励在cpp文件中使用具名命名空间

  • 不应该使用using引入整个命名空间

    //禁止把整个命名空间引入 -- 会污染命名空间
    using namespace foo;
    
  • 不应该在头文件使用命名空间别名

    //禁止使用别名命名空间在头文件中 -- 会导致别名命名空间成为公开API的一部分
    namespace baz = ::foo::bar::baz;
    
  • 禁止使用内联命名空间

    //禁止使用内联 -- 会带来API迷惑
    inline namespace FOO {
    
    }
    

局部变量

将函数变量尽量置于最小作用域内,并对变量声明时进行初始化。

构造函数

  • 不要在构造函数中调用虚函数
  • 不要在构造函数中进行可能失败的初始化

默认构造函数

如果一个类定义了若干个成员变量又没有其他构造函数,必须定义一个默认构造函数。否则编译器会自动产生一个很糟糕的默认构造函数。

显式构造函数

对于单个参数的构造函数,使用关键字explicit.

拷贝构造函数

仅在一个类需要拷贝一个类的对象时才使用拷贝构造函数。大部分情况下不需要拷贝的类,应该使用DISALLOW_COPY_AND_ASSIGN.

DISALLOW_COPY_AND_ASSIGN的使用方法如下:

#define DISALLOW_COPY_AND_ASSIGN(TypeName) \
            TypeName(const TypeName&); \
            void operator=(const TypeName&)

//把拷贝构造和赋值操作声明为private
class Foo {
    public:
        Foo(int f);
        ~Foo();
    
    private:
        DISALLOW_COPY_AND_ASSIGN(Foo);
};

结构体 VS. 类

仅当数据时使用struct。否则使用class

继承

使用组合常常比使用继承更合理。

如果使用继承,一般定义为public继承

多重继承

真正需要多重继承的情况少之又少。

只有一种情况允许使用多重继承:最多只有一个基类时非抽象类,其他基类都是抽象类

运算符重载

只有在少数特定情况下使用。

否则尽量不要使用运算符重载

成员变量

将所有数据成员变量声明为private.。

除非是 static const 类型成员

声明顺序

一般应该以public开始,后跟protected,最有是private

将类似的声明放在一起

函数

参数顺序

输入参数在先,后跟输出参数

编写简短函数

函数一般要求不超过40行

引用输入参数

输入型的引用参数,必须加上const

例如

void foo(const string &in, string& out);

缺省参数

建议使用缺省参数,但不允许在虚函数中使用

语言特性

变长数组和alloca()

不允许使用变长数组和alloca()

异常

不使用异常

运行时类型识别

禁止使用RTTI

RTTI允许程序员通过typeid或者dynamic_cast在运行时识别C++对象的类型

前置自增和自减

对于自增(++i 或 i++),如果自增后,返回值没有被用到,前置自增(++i)要比后置自增(i++)效率更高

const用法

强烈建议在任何可能的情况下使用const

预处理宏

使用宏时需要非常小心,尽量用内联函数、枚举和常量代替

0,nullptr和NULL

整数用0,实数用0.0,指针用nullptr或者NULL,字符用'\0'

sizeof

尽量用sizeof(varname)代替sizeof(type)

模板编程

不要使用复杂的模板编程

命名约定

通用命名规则

  • 函数命名,变量命名,文件命名应该具备描述性,不要过度缩写
  • 类型和变量应该是名词
  • 函数应该是动词

好的命名:

int num_errors; // Good.
int num_completed_connections; // Good.
int price_count_reader; // 无缩写
int num_errors; // "num" 是一个常见的写法
int num_dns_connections; // 人人都知道 "DNS" 是什么

不好的命名:

int n; // 毫无意义.
int nerr; // 含糊不清的缩写.
int n_comp_conns; // 含糊不清的缩写.
int wgc_connections; // 只有贵团队知道是什么意思.
int pc_reader; // "pc" 有太多可能的解释了.
int cstmr_id; // 删减了若干字母.

文件命名

文件名要全部小写,可以包含下划线 (_) 或者连字符 (-)

例如:

my_userful_class.cpp

类命名

类名字的每个单词首字母大写,不包含下划线

例如:MyExcitingClass

变量命名

变量和数据成员名使用小写,单词间使用下划线连接,类成员变量以下划线结尾

例如变量:

string table_name;

例如类数据成员

class Foo {
   ...
   private:
     string table_name_;
};

常量命名

命名以"k"开头,大小写混合,例如:

const int kDaysInAWeek = 7;

函数命名

常规函数使用大小写混合,例如

void OnMessage();

取值和设值函数,使用变量名字来命名,例如:

class Foo {
    public:
        int num_entries() const { return num_entries_;}
    private:
        int num_entries_;
};

命名空间命名

以大小写字母命名

枚举命名

使用常量或宏的命名方法命名

例如:

enum UrlTableErrors {
 kOK = 0,
 kErrorOutOfMemory,
 kErrorMalformedInput,
};
enum AlternateUrlTableErrors {
 OK = 0,
 OUT_OF_MEMORY = 1,
 MALFORMED_INPUT = 2,
};

宏命名

使用大写字母和下划线来命名

注释

类注释

  • 描述类用法的注释应该放在头文件
  • 描述类的实现的注释应该放在实现文件中

函数注释

函数声明的注释内容:

  • 函数的输入输出
  • 函数调用期间,是否会释放某些参数
  • 如果函数分配了空间,需要由调用者释放
  • 参数是否可以为空指针
  • 是否存在性能隐患
  • 函数是否可重入

变量注释

通常根据变量名就可以说明变量用途

实现注释

对于代码中晦涩,重要的地方加以注释

格式

行长度

每行代码不超过80个字符

空格还是制表位

只是用空格,每次缩进2个空格

函数声明和定义

返回类型和函数名在同一行,参数也尽量在同一样,否则参数分行放置

引用自(谷歌C++编程规范)

https://zh-google-styleguide.readthedocs.io/en/latest/

©著作权归作者所有,转载或内容合作请联系作者
禁止转载,如需转载请通过简信或评论联系作者。
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 204,445评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,889评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,047评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,760评论 1 276
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,745评论 5 367
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,638评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,011评论 3 398
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,669评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,923评论 1 299
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,655评论 2 321
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,740评论 1 330
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,406评论 4 320
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,995评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,961评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,197评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,023评论 2 350
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,483评论 2 342

推荐阅读更多精彩内容

  • 引言 C++ 是一门十分复杂并且威力强大的语言,使用这门语言的时候我们应该有所节制,绝对的自由意味着混乱。 1.规...
    NHFX阅读 531评论 0 1
  • 序 C++用法很多,包容性也比较强。一个C++的工程可能包含了各种各样没见过的用法。本篇内容主要是参照谷歌C++标...
    开源519阅读 1,971评论 0 1
  • 组织 不要拘泥于小节 在高警告级别干净利落地进行编译 使用自动构建系统 适用版本控制系统 在代码审查上投入 设计 ...
    赵者也阅读 465评论 0 1
  • 缩进 采用四个空格,不要TAB 大括号 函数内部控制语句左大括号不换行 变量/成员变量 小驼峰,类成员变量(静态和...
    土豆吞噬者阅读 985评论 0 0
  • 前言 这是自己整理出的一套自己正在使用的编程规范,并没有涵盖有所东西,只有一些自己常用或正在使用的,后面也许会...
    你怎么还没玩累啊阅读 478评论 0 0