公司C++规范学习

公司C++规范学习

语法部分

  • class和struct关键字的选择:class表示被封装的用户自定义类型,不公开定义非静态数据成员,struct表示数据的简单集合,只定义用于初始化数据成员的方法。
  • 必须使用构造函数初始化列表显示初始化直接基类与所有基类类型数据成员。
  • 没有复制意义的类必须用DISALLOW_COPY_AND_ASSIGN宏禁止拷贝构造函数和赋值构造函数。
    • DISALLOW_COPY_AND_ASSIGN 宏就是将复制拷贝函数和赋值操作符声明为私有变量。
  • 禁止在构造函数中进行可能出错的复杂操作(比如申请资源),复杂操作用额外的init()函数实现。
  • 显式初始化能够使代码更清晰、不易错误调用,还能避免二次赋值造成的效率问题, 单参构造函数尽量加上explicit。
  • 有默认值语义的类必须显示定义默认构造函数。
  • 没有默认值语意的类,必须显式定义其它构造函数或 private 声明默认构造函数,并不给出实现。
  • 如果类型是可拷贝,一定要同时定义拷贝构造函数和赋值构造函数,如果类型可移动,一定要同时定义移动构造函数和移动赋值函数。
    • 如果使用默认的拷贝和移动操作,要使用=default定义。
    • 如果类型不需要拷贝、移动操作,要使用=delete手段禁用。
    • 向容器添加数据时,优先使用emplace开头的接口函数。
  • 在极少的情况下,当该类的隐式转换很有意义时,可以不把单参数构造函数声明为显式构造函数,但必须十分小心,并使用文档化注释说明。
  • 单参数构造函数如果不用explicit关键字修饰,则可能被编译器用来做隐式类型转换。
  • 有复制意义的类必须显式给出复制构造函数,并小心指定其行为(浅复制、深复制等)
    • 托管了资源的类,往往是没有复制意义的。此时应当防止用户错误调用而导致资源泄漏、重复释放的后果。
    • 编译器默认生成的复制构造函数,对指针数据成员使用浅复制策略,但这种策略常常不是程序员希望的。
  • 析构函数:
    • 若类定义了虚函数,必须定义虚析构函数。
    • 若类设计为可被继承的,应该定义公开的虚析构函数或保护的非虚析构函数。
    • [RULE010] (不包括结构)含有指针成员,必须显式给出析构函数,并小心指定其行为(是否销毁指针,如何销毁等).
    • 绝不允许让异常离开析构函数。
  • 按照 public、protected、private 的顺序声明成员。按照类型、方法、数据成员的顺序声明成员, DISALLOW_COPY_AND_ASSIGN 放在 private 区段的末尾.
  • 不是非常必要的话,避免使用友元: 友元破坏了类的封装性,增加了类之间的耦合,适宜使用友元的如容器与它的迭代器,类与涉及该类的.
  • 委派和继承构造函数是由 C++11 引进为了减少构造函数重复代码而开发的两种不同的特性. 通过特殊的初始化列表语法, 委派构造函数允许类的一个构造函数调用其他的构造函数.
  • C++11强类型枚举enum class Side
  • 被重载的虚函数通过override显示声明.
  • 禁止被重载的函数通过final显示禁止.
  • Lambda表达式:
    • 不使用默认捕获(包括&, =), 捕获显示写出来.
  • 只在两种情况下使用右值引用, 一种是定义类型的移动操作函数时, 另一种是定义模板函数实现完美转发的时候. 除此之外, 不要使用右值引用.
    • 右值引用的语义比较复杂, 不恰当的使用会造成很难追查的bug.
  • 建议 include 的路径、文件名与命名空间保持一致
  • 使用空格缩进,不使用制表符。 以 4 个空格为单位缩进。避免超过 5 重的缩进。
  • 建议不要使用异常,除非已有项目/底层库使用了异常, 这时候必须要catch所有异常。
  • 所有代码应该定义在namespace中, main函数除外。
  • 禁止内联(inline)虚函数,inline函数应该在10行以内。
  • 函数静止使用默认参数,模板可以使用默认参数。-- 默认参数是编译期绑定的,不具有多态性。
  • 使用 auto 自动推导类型, 只能定义局部变量(返回类型后置声明除外).
  • 禁止使用 auto_ptr. 如果需要传递对象, 使用std::unique_ptr明确所有权传递. 如果要共享所有权, 使用std::shared_ptr明确所有权共享.
  • [RULE033] 避免对浮点数进行相等或不等比较。
    • 避免使用非布尔型的变量或表达式作为分支语句条件。
    • 作相等比较时,建议把常量或右值变量放在 == 运算符的右边。
  • 除非用于条件编译、跟踪调试,或者能够显著减少代码量并确保可读性的宏(如 DISALLOW_COPY_AND_ASSIGN, CFATAL_LOG etc.),否则不应使用。
  • 鼓励使用前向声明,以减少文件之间的依赖关系。
  • 尽可能避免使用全局变量,如有必要应使用 singleton 模式代替。
    • 内部使用的全局函数/变量,必须声明为静态函数、变量,不能在目标文件中出现外部可见的符号。
  • 不应在头文件定义类/结构体类型的全局常量,以减少代码膨胀。
  • 可能用于跨模块间通讯或者涉及存储的枚举值必须显式指定值,避免版本不一致造成诡异的错误。
  • const 关键字用在类型名前面而非后面。
    • 不修改内部状态的成员方法必须声明为 const
    • 返回不可修改的指针或引用必须声明为 const
    • 不被修改的引用/指针形参必须声明为 const
    • 除非保证状态不会被修改,不应使用 const_cast 来去掉 const 属性
  • 避免定义超过 4 KB 的局部变量数组

风格/约定

  • 必须按以下顺序引用头文件,并进行分节:.cpp 对应的头文件(如果有, // 优先位置), C(标准)库,C++(标准)库,其它库,自己项目的.h文件,每节内的include按照字母序。
  • 分行与空格:
    • 一条单独的语句必须独立成行。
    • 避免连续的空行。
    • 使用适当的空行来分组代码的逻辑。
    • 左大括号不独立,右大括号独立成行。并且左大括号所在行进行垂直对齐。
  • 命名空间不缩进。
  • 构造函数初始化列表放在同一行或按至少八格缩进并排几行, 如果需要换行, 把: 放在第一行。
  • 比较短的函数声明,整个声明占一行;过长的函数声明,令每一参数占一行,并且垂直对齐, 换行后的参数至少保持 8 个空格缩进。
  • 比较短的函数调用语句,整个语句占一行;过长的函数调用,控制折行确保每行不要超过 100 列,换行后至少额外缩进 8 个空格。
  • 空格的使用:
    • if/switch/while/for/catch 与后边的圆括号之间加一个空格,圆括号内侧与判断表达式之间不加空格。
    • for语句圆括号内分号前不加空格,分号后加一个空格。
    • 函数调用中,函数名和圆括号之间不要加空格。
    • 类继承与构造函数的初始化列表的冒号前后加一个空格。
    • 逗号表达式或参数列表中,逗号前不加空格,逗号后加一个空格。
    • 一元运算符前后不加空格。二元、三元表达式前后各加一个空格。
    • 成员访问或作用域运算符前后不加空格。如:a.b, a->b, a.b, a->b, a::b。
    • 圆括号和方括号运算符前后和内部都不加空格。
  • .h 中不要定义复杂的函数, 复杂函数如果必须在头文件中, 应该放在 .hpp 后缀的头文件中。
  • 没有使用到的函数参数,把参数名注释起来, 不允许未命名参数出现。
    • :函数的参数顺序,建议先安排输入参数,再安排输出参数。
    // 输入参数对象类型使用const引用
    void foo(const std::string& input1, const MyClass& input2);
    

命名规范

  • 全局可见的,却又无法通过命名空间约束的标识符命名,必须以库名作为前缀,以避免冲突。

  • 命名应当尽可能有描述性,不使用非通用的缩写(尤其是省一个字符的缩写如creat,usr等),不使用有歧义的缩写,不使用任意的无意义的字符.

  • 标识符的作用域越大,命名就应该越清晰。

  • 可能使用多个单位名称的变量(如表示时间的变量),应以单位名称缩写为后缀。

  • 文件名全部用小写字母, 中间用_间隔; 比如: this_is_my_awesome_file.cpp.

  • 单元测试文件名使用<被测文件名>_test.h(.cpp)命名.

  • 如果是要发布供它人使用的 lib,推荐仅暴露一个以库名同名,或者<库名>_<功能集合>.h的 api 头文件,然后将相关头文件 include 到这个 api 头文件中。如:mylib.h,mylib_utils.h,mylib_api.h。

  • 使用下划线分隔的全小写命名法命名命名空间。

  • 命名空间名可以使用缩写,同时应保证简短、不易冲突但同时富有意义。

  • 函数命名使用下划线分隔的全小写命名法。

  • 函数通常使用动词短语命名。

  • 自定义类类型使用首字母大写的驼峰命名法命名,一般不使用前缀。

  • 枚举类型成员,使用全大写蛇形命名法(即全部字母大写,单词间用下划线分隔)。

  • 尽可能不使用全局变量,如果必须使用,必须以g为前缀,而且必须足够长以避免名字冲突。

  • 全局变量使用下划线分隔的全小写命名法命名。

  • 局部变量名使用下划线分隔的全小写命名法命名。

    • 假如局部变量作用域很小,可以适当使用缩写。
  • 全局静态变量 s_ 作为前缀。类静态成员使用 s 前缀的下划线分隔的全小写命名法命名。

  • const 常量与枚举常量都使用下划线分隔的全大写命名法命名

  • 确定需要不同大小的变量,推荐使用<stdint.h>中定义的长度明确的整形类型,例如 int64_t, int32_t 等。

  • 多行注释必须写在被解释内容的上方。单行注释可以写在被注释语句的上文或右方。

  • 一行内只应声明一个变量。

  • 局部变量应尽量延迟到第一次使用处声明。

  • 局部变量应在声明处赋初值,指针类型局部变量必须在声明处赋初值。

  • 本规范不适用于引入的公司外第三方代码, 修改时请遵守第三方代码自身的编码风格。

  • 显式或者通过宏等隐式的定义派生自第三方代码中所申明的类,必须遵守对应第三方代码自身的编码风格。

posted @ 2019-06-13 14:50  coding-for-self  阅读(1065)  评论(1编辑  收藏  举报