当前位置:   article > 正文

C++项目编码规范(Huawei&Google合辑)_华为编程规范5.0

华为编程规范5.0

c++ 项目编码规范

文档说明

本文档适用于所有 c++ 项目,涵盖命名风格、注释风格等内容,文档内容大部分来源于Google发布的 c++ 开源项目风格指南华为c++编程风格指南。

1. 命名约定

最重要的一致性规则是命名管理。命名的风格能让我们在不需要去查找类型声明的条件下快速地了解某个名字代表的含义: 类型, 变量, 函数, 常量, 宏, 等等,我们大脑中的模式匹配引擎非常依赖这些命名规则.

1.1 通用命名规则

总述

函数命名, 变量命名, 文件命名要有描述性; 少用缩写。

说明

尽可能使用描述性的命名, 别心疼空间, 毕竟相比之下让代码易于新读者理解更重要. 不要用只有项目开发者能理解的缩写, 也不要通过砍掉几个字母来缩写单词。

1.2 文件命名

每一个C++类必须使用一个独立的源文件进行书写(一个.h文件和一个.cpp文件);并且要求文件名必须与类名相同。

1.3 类型命名

类型名称的每个单词首字母均大写, 不包含下划线:如 MyExcitingClass, MyExcitingEnum。

1.4 变量命名

总述

变量 (包括函数参数) 和数据成员名一律小写, 单词之间用下划线连接. 类的成员变量以下划线结尾, 但结构体的就不用, 如: a_local_variable, a_struct_data_member, a_class_data_member_。

说明
普通变量说明
string table_name; // 好,全小写,下划线分隔
string tableName; // 差,大小写混合
  • 1
  • 2
类数据成员说明

不管是静态的还是非静态的, 类数据成员都可以和普通变量一样, 但要接下划线.

class TableInfo {
  ...
 private:
  string table_name_;  // 好 - 后加下划线.
  string tablename_;   // 好.
  static Pool<TableInfo>* pool_;  // 好.
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
结构体变量说明

不管是静态的还是非静态的, 结构体数据成员都可以和普通变量一样, 不用像类那样接下划线:

struct UrlTableProperties {
  string name;
  int num_entries;
  static Pool<UrlTableProperties>* pool;
};
  • 1
  • 2
  • 3
  • 4
  • 5

1.5 常量命名

总述

声明为 constexpr 或 const 的变量, 或在程序运行期间其值始终保持不变的, 命名时以 “k” 开头, 大小写混合. 例如:

// c一般用来代指char,所以用k来代表const。
const int kDataPort = 60001;
static int kInputNums = 0;
  • 1
  • 2
  • 3
说明

所有具有静态存储类型的变量 (例如静态变量或全局变量) 都应当以此方式命名. 类的静态成员变量除外。

1.6 函数命名

使用驼峰法命名,尽量不含有下划线。如 sendMsg, sendFile 等。

1.7 宏命名

你并不打算 使用宏, 对吧? 如果你一定要用, 像这样命名(全大写,下划线分隔): MY_MACRO_THAT_SCARES_SMALL_CHILDREN.

1.8 枚举命名

枚举的命名应当和常量或宏一致: kEnumName 或是 ENUM_NAME。本规范建议与宏命名规则一致。

enum AlternateUrlTableErrors {
    OK = 0,
    OUT_OF_MEMORY = 1,
    MALFORMED_INPUT = 2,
};
  • 1
  • 2
  • 3
  • 4
  • 5

2. 注释约定

注释虽然写起来很痛苦, 但对保证代码可读性至关重要。下面的规则描述了如何注释以及在哪儿注释。 当然也要记住: 注释固然很重要, 但最好的代码应当本身就是文档。 有意义的类型名和变量名, 要远胜过要用注释解释的含糊不清的名字。

你写的注释是给代码读者看的, 也就是下一个需要理解你的代码的人。 所以慷慨些吧, 下一个读者可能就是你!

2.1 注释风格

使用 // 或 /* */, 统一就好。
推荐文件注释、函数头部注释使用 /* */ 风格, 函数内单行注释使用双斜线风格。

2.2 文件注释

在每一个文件开头加入版权公告和文件说明,注意文件说明大致包括作者,创建日期,内容概述如

/* Copyright (c) 2020 aircas. All rights reserved. */
/**
* @Author: Wolf
* @brief: 协议编码工厂类,包含对应帧编码工厂函数,完成
 *        1.短消息  2.短消息确认帧  3.长消息  4.长消息确认帧  5.文件元信息帧   6.文件元信息确认帧
 *        7.文件数据帧   8.文件数据确认帧  9.结束帧   10.成功帧
 *        的编码,并返回指向对应帧缓冲区的指针,用于网络传输
 *
 *    编码过程简述:
 *    1.对应工厂函数首先计算各(负载 + 协议)的开销,并以此长度构造buffer,
 *    2.填充子协议数据域,进行子协议部分编码
 *    3.完成标准协议编码
 * @version: 版本编号  修订日期  修订者 修订内容 (最新的在最前面)
*/
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14

其中的 2020 表示编码时间。如果是时间段,用中划线分隔。

内容

如果一个 .h 文件声明了多个概念, 则文件注释应当对文件的内容做一个大致的说明, 同时说明各概念之间的联系. 一个一到两行的文件注释就足够了, 对于每个概念的详细文档应当放在各个概念中, 而不是文件注释中.

2.3 类注释

总述

每个类的定义都要附带一份注释, 描述类的功能和用法, 除非它的功能相当明显.

// Iterates over the contents of a GargantuanTable.
// Example:
//    GargantuanTableIterator* iter = table->NewIterator();
//    for (iter->Seek("foo"); !iter->done(); iter->Next()) {
//      process(iter->key(), iter->value());
//    }
//    delete iter;
class GargantuanTableIterator {
  ...
};
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
说明

类注释应当为读者理解如何使用与何时使用类提供足够的信息, 同时应当提醒读者在正确使用此类时应当考虑的因素. 如果类有任何同步前提, 请用文档说明. 如果该类的实例可被多线程访问, 要特别注意文档说明多线程环境下相关的规则和常量使用.

如果你想用一小段代码演示这个类的基本用法或通常用法, 放在类注释里也非常合适.

如果类的声明和定义分开了(例如分别放在了 .h 和 .cc 文件中), 此时, 描述类用法的注释应当和接口定义放在一起, 描述类的操作和实现的注释应当和实现放在一起.

2.4 函数注释

总述

函数声明处的注释描述函数功能; 定义处的注释描述函数实现.

函数声明

基本上每个函数声明处前都应当加上注释, 描述函数的功能和用途. 只有在函数的功能简单而明显时才能省略这些注释(例如, 简单的取值和设值函数). 注释使用叙述式 (“Opens the file”) 而非指令式 (“Open the file”); 注释只是为了描述函数, 而不是命令函数做什么. 通常, 注释不会描述函数如何工作. 那是函数定义部分的事情.

函数声明处注释的内容:

函数的输入输出.
对类成员函数而言: 函数调用期间对象是否需要保持引用参数, 是否会释放这些参数.
函数是否分配了必须由调用者释放的空间.
参数是否可以为空指针.
是否存在函数使用上的性能隐患.
如果函数是可重入的, 其同步前提是什么?
举例如下:

// Returns an iterator for this table.  It is the client's
// responsibility to delete the iterator when it is done with it,
// and it must not use the iterator once the GargantuanTable object
// on which the iterator was created has been deleted.
//
// The iterator is initially positioned at the beginning of the table.
//
// This method is equivalent to:
//    Iterator* iter = table->NewIterator();
//    iter->Seek("");
//    return iter;
// If you are going to immediately seek to another place in the
// returned iterator, it will be faster to use NewIterator()
// and avoid the extra seek.
Iterator* GetIterator() const;
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

但也要避免罗罗嗦嗦, 或者对显而易见的内容进行说明. 下面的注释就没有必要加上 “否则返回 false”, 因为已经暗含其中了:

// Returns true if the table cannot hold any more entries.
bool IsTableFull();
  • 1
  • 2

注释函数重载时, 注释的重点应该是函数中被重载的部分, 而不是简单的重复被重载的函数的注释. 多数情况下, 函数重载不需要额外的文档, 因此也没有必要加上注释.

注释构造/析构函数时, 切记读代码的人知道构造/析构函数的功能, 所以 “销毁这一对象” 这样的注释是没有意义的. 你应当注明的是注明构造函数对参数做了什么 (例如, 是否取得指针所有权) 以及析构函数清理了什么. 如果都是些无关紧要的内容, 直接省掉注释. 析构函数前没有注释是很正常的.

函数定义

如果函数的实现过程中用到了很巧妙的方式, 那么在函数定义处应当加上解释性的注释. 例如, 你所使用的编程技巧, 实现的大致步骤, 或解释如此实现的理由. 举个例子, 你可以说明为什么函数的前半部分要加锁而后半部分不需要.

不要 从 .h 文件或其他地方的函数声明处直接复制注释. 简要重述函数功能是可以的, 但注释重点要放在如何实现上.

2.5 实现注释

对于代码中巧妙的, 晦涩的, 有趣的, 重要的地方加以注释.

2.6 todo 注释

对那些临时的, 短期的解决方案, 或已经够好但仍不完美的代码使用 todo 注释.

todo 注释风格:双斜杠,空格,todo,左括号,时间(如2020/3/21,必须用此格式),空格,作者名,右括号,冒号,空格,注释内容。如果注释超过一行,则第二行仍旧以上述风格开头。

// todo(时间 作者): 注释内容
// todo(2020/3/21 wolf): 注释内容(XXXXXXXXXXXXXXX一行不够写)
// todo(2020/3/21 wolf): XXX这里是上一行注释的延续
  • 1
  • 2
  • 3

如果加 todo是为了在 “将来某一天做某事”, 可以附上一个非常明确的时间 “Fix by November 2005”), 或者一个明确的事项 (“Remove this code when all clients can handle XML responses.”).

2.7 不允许的行为

不要描述显而易见的现象, 永远不要 用自然语言翻译代码作为注释, 除非即使对深入理解 C++ 的读者来说代码的行为都是不明显的. 要假设读代码的人 C++ 水平比你高, 即便他/她可能不知道你的用意:

你所提供的注释应当解释代码 为什么 要这么做和代码的目的, 或者最好是让代码自文档化.

比较这样的注释:

// Find the element in the vector.  <-- 差: 这太明显了!
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) 
{
  Process(element);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

和这样的注释:

// Process "element" unless it was already processed.
auto iter = std::find(v.begin(), v.end(), element);
if (iter != v.end()) 
{
  Process(element);
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

自文档化的代码根本就不需要注释. 上面例子中的注释对下面的代码来说就是毫无必要的:

if (!IsAlreadyProcessed(element)) 
{
  Process(element);
}
  • 1
  • 2
  • 3
  • 4

3. 附加规则

  1. 缩进风格:我们使用空格缩进(四格),不要在代码中使用制表符。你应该设置编辑器将制表符转为空格。

  2. 单行代码长度最大为100个半角英文字符,超过最大长度需主动换行。

  3. 相对独立的程序块之间、变量说明之后必须加空行(一个空行即可)。

  4. 程序块的分界符(如C/C++语言的大括号‘{’和‘}’应各独占一行且位于同一列,同时与引用它们的语句左对齐。在函数体的开始、类的定义、结构体的定义、枚举的定义以及if、for、do、while、switch、case语句中的程序都要采用缩进方式。

    // 分界符独占一行原因:直观,方便调试
    void sayHello (bool is_dog)
    {
    	for (int i = 0; i < 10; ++i)
    	{	
    		if (is_dog)
        	{
            	std::cout << "wang wang" << std::endl;
       		}
            else 
            {
                std::cout << "hello" << std::endl;
            }
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
  5. 在两个以上的关键字、变量、常量之间进行对等操作时,它们之间的操作符之前或者前后要加空格;进行非对等操作时,如(->),后面不应加空格。

    // 逗号,分号只在后面加空格,如:
    int num = 0, toal = 0;
    
    // 比较操作符,赋值操作符“=”、“+=”,算术操作符“+”、“%”,逻辑操作符“&&”、“&”,位域操作符”<<”、“^”等 双目操作符前后都要加空格
    // if、for、while、switch等与后面的括号间应该加空格,使if等关键字更为突出、明显,如:
    for (int i = 0; i < 10; ++i)
    {
    	std::cout << "hello" << std::endl;
    }
    
    // "!"、"~"、"++"、"--"、"&"(地址运算符)等单目操作符前后不加空格。
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
  6. 一行只应写一条语句。

  7. if、for、do、while、switch等语句的执行语句部分,无论多少都要加括号{}。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/2023面试高手/article/detail/240819
推荐阅读
相关标签
  

闽ICP备14008679号