一、命名规范
1、目录/文件
规范:使用小写字母、数字、下划线,多个单词间用下划线分隔。
原因:Windows对目录和文件是大小写不区分的,而Linux是大小写区分的。为了达到平台统一,这里规定都使用小写字母。
示例:
目录:app_cxx/proc_ui/src/firewall
文件:firewall_mgr.h、main.cpp、nfsv2.cpp
2、函数/接口
规范:使用小驼峰命名法。
原因:大部分著名的开源项目以及大厂对外提供的API都是这样的命名规范。
示例:
int test()
{
return 0;
}
int getLocalPort()
{
return 4335;
}
3、命名空间
规范:使用小写字母、数字、下划线,多个单词间用下划线分隔 (尽量只使用一个单词),
示例:
namespace logger
{
}
4、结构体/类
规范:使用大驼峰命名法,不包含下划线
示例:
struct SendData
{
};
class LoggerManager
{
};
5、变量
规范:使用小驼峰命名法。
(1)局部变量
示例:
void test()
{
int reqNum = 0;
}
(2)全局变量
补充:需要加前缀g_
示例:
int g_reqNum= 0;
(3)静态变量
补充:需要加前缀s_
示例:
void test()
{
static int s_reqNum = 0;
}
补充:当它同时也是全变量时使用全局变量的命名方式
示例:
int g_reqNum = 0;
(4)类成员变量
补充:需要加前缀m_
示例:
class ServerInfo
{
private:
int m_reqNum
}
(5)类静态变量
补充:需要加前缀s_
示例:
class ServerInfo
{
private:
static int s_reqNum;
}
(6)常量
补充:需要大写字母
示例:
const int MAX_NUM = 1024;
5、宏定义
规范:需要大写字母
示例:
#define CACHE_PATH "/data/cache"
6、枚举
规范:枚举名使用大驼峰命名法,枚举名建议带上class类型,枚举值需要小写字母
原因:避免和宏定义冲突
示例:
enum class ResultType
{
ok,
open_fail,
unknown
};
二、排版规范
1、缩进
程序块要采用缩进风格编写,缩进的空格数为 4 个
2、 独立说明加空行
相对独立的程序块之间、变量说明之后必须加空行
示例:
if (!valid_ni(ni))
{
... // program code
}
repssn_ind = ssn_data[index].repssn_index;
repssn_ni = ssn_data[index].ni;
3、 字符 80+ 多行书写
较长语句(>80字符)分多行书写,长表达式要在低优先级操作符处划分新行,操作符放在新行之首,划分出的新行要适当缩进
perm_count_msg.head.len = NO7_TO_STAT_PERM_COUNT_LEN
+ STAT_SIZE_PER_FRAM * sizeof( _UL );
act_task_table[frame_id * STAT_TASK_CHECK_NUMBER + index].occupied
= stat_poi[index].occupied;
report_or_not_flag = ((taskno < MAX_ACT_TASK_NUMBER)
&& (n7stat_stat_item_valid (stat_item))
&& (act_task_table[taskno].result_data != 0));
4、 较长语句进行划分
循环、判断等语句中若有较长的表达式或语句,要进行适应划分,长表达式要在低优先级操作符处划分新行,操作符放在新行之首
示例:
if ((taskno < max_act_task_number)
&& (n7stat_stat_item_valid (stat_item)))
{
... // program code
}
for (i = 0, j = 0; (i < BufferKeyword[word_index].word_length)
&& (j < NewKeyword.word_length); i++, j++)
{
... // program code
}
for (i = 0, j = 0;
(i < first_word_length) && (j < second_word_length);
i++, j++)
{
... // program code
}
5、 内嵌语句独占且加括号
if、for、do、while、case、switch、default 等语句自占一行,且 if、for、do、while 等的执行语句部分要加括号{ }
if (pUserCR == NULL)
{
return;
}
6、 空格对齐
对齐只使用空格键,不使用TAB键。
// 说明:以免用不同的编辑器阅读程序时,因 TAB 键所设置的空格数目不同而造成程序布局
// 不整齐,不要使用 BC 作为编辑器合版本,因为 BC 会自动将 8 个空格变为一个 TAB 键,
// 因此使用 BC 合入的版本大多会将缩进变乱。
7、 操作符与空格
在两个以上关键字、变量、常量对等操作时,之间的操作符之前、之后或前后加空格;非对等操作时,关系密切的立即操作符(如->)后不加空格
(1)“!”、“~”、“++”、“–”、“&”(地址运算符)等单目操作符前后不加空格。
*p = 'a'; // 内容操作"*"与内容之间
flag = !isEmpty; // 非操作"!"与内容之间
p = &mem; // 地址操作"&" 与内容之间
i++; // "++","--"与内容之间
(2)“->”、"."前后不加空格。
p->id = pid; // "->"指针前后不加空格
(3) if、for、while、switch 等与后面的括号间应加空格,使 if 等关键字更为突出、明显。
if (a >= b && c > d)
三、注释规范
规范:尽量用/* */进行注释,避免使用//。
原因:使用//时,当不小心把其下面的一行代码回退到//所在行时,如果没注意的话容易造成逻辑出错。
1 、说明文件
说明性文件(.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应注释,必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,还应有函数功能简要说明。
/***************************************************
Copyright (C), Your Company
File name: // 文件名
Author: Version: Date: // 作者、版本及完成日期
Description: // 用于详细说明此程序文件完成的主要功能,与其他模块
// 或函数的接口,输出值、取值范围、含义及参数间的控
// 制、顺序、独立或依赖等关系
Others: // 其它内容的说明
Function List: // 主要函数列表,每条记录应包括函数名及功能简要说明
1. ....
History: // 修改历史记录列表,每条修改记录应包括修改日期、修改
// 者及修改内容简述
1. Date:
Author:
Modification:
2. ...
***************************************************/
2、 源文件
源文件头部应注释,列出:版权说明、版本号、生成日期、作者、模块目的/功能、主要函数及其功能、修改日志等。
/************************************************************
Copyright (C), 1988-1999, Huawei Tech. Co., Ltd.
FileName: test.cpp
Author: Version : Date:
Description: // 模块描述
Version: // 版本信息
Function List: // 主要函数及其功能
1. -------
History: // 历史修改记录
<author> <time> <version > <desc>
David 96/10/12 1.0 build this moudle
***********************************************************/
说明:Description 一项描述本文件的内容、功能、内部各部分之间的关系及本文件与
其它文件关系等。History 是修改历史记录列表,每条修改记录应包括修改日期、修改
者及修改内容简述
3、结构体/类注释
示例:
/**
* @brief 用户信息集合
*/
struct UserInfo
{
};
4、接口注释
示例:
/**
* @brief 查询用户信息
* @param username 用户名
* @return 用户信息
*/
UserInfo queryUserInfo(const std::string& username);
5、变量注释
示例:
int age = 0; /* 年龄 */
四、设计规范
每个 C/C++ 程序通常分为两个文件。一个文件用于保存程序的声明(declaration),称为头文件。另一个文件用于保存程序的实现(implementation),称为定义(definition)文件。
C/C++ 程序的头文件通常以 “.h” 为后缀,C 程序的定义文件以 “.c” 为后缀,C++ 程序的定义文件通常以 “.cpp/.cc” 为后缀。
1、 头文件的结构
- 为了防止头文件被重复引用,应当在头文件的第一行添加 #pragma once,也可以用 ifndef/define/endif 结构产生预处理块
- 用 #include <filename.h> 格式来引用标准库的头文件
编译器将在标准库目录搜索,该目录保存在系统的环境变量中 - 用 #include \“filename.h” 格式来引用非标准库的头文件
编译器将在当前的工作目录搜索,如果找不到会到标准库目录搜索 - 头文件中只存放声明,而不存放定义
#prama once // 防止头文件被重复包含
#include <stdio.h> // 引用标准库的头文件
#include "myheader.h" // 引用非标准库的头文件
void Function(...); // 全局函数声明
struct ClassName { // 类结构声明
...
};
2、定义文件的结构
定义文件有两部分内容:
对头文件的引用
程序的实现体(包括数据和代码)
#include "myheader.h" // 引用头文件
// 全局函数的实现
void Function(...) {
...
}
// 类成员函数的实现
void ClassName::Fun(...) {
...
}
3、声明/定义的位置
规范:宏/枚举/变量/接口的声明和定义需要就近原则,能不跨文件就不跨文件。
原因:使得代码更加内聚,提升代码阅读的便利性。
示例:
在类DbMgr中使用了宏定义:#define QUEYR_OK 1
- 如果该宏只在源文件DbMgr.cpp中使用,则宏定义放在DbMgr.cpp中。
- 如果该宏仅仅作为返回值透露到外部模块,则宏定义放在DbMgr.h中。
- 如果该宏在其他若干业务中频繁使用,则宏定义放在公共的业务枚举定义文件中(可能如:enum_def.h)。
4、结构体(struct) or 类(class)?
规范:当定义的类型只是数据的集合且内部无复杂的处理逻辑时选择用struct,反之如果更注重行为的封装则选择class。
原因:struct存在于栈上,用完即销毁。 注意: 由于栈有容量限制,避免在struct中存放超大量的值类型。
示例:
struct UserInfo
{
UserInfo() : age(0), sex(0) {}
std::string username;
int age;
int sex;
};
class Grade
{
public:
std::vector<UserInfo> queryUserInfoList(const std::string& username);
};
5、单文件(.hpp) or 多文件(.h,.cpp) ?
规范:当模块只是一些信息的集合且无复杂内部逻辑可以使用.hpp,反之使用多文件方式。
原因:模块逻辑复杂时,会经常变动,如果引用的外部模块多而使用.hpp时,编译时间会变长。
示例:
单文件user_info.hpp
#pragma once
#include <string>
struct UserInfo
{
UserInfo() : age(0), sex(0) {}
std::string username;
int age;
int sex;
};
多文件grade.h、grade.cpp
#pragma once
#include "user_info.h"
class Grade
{
public:
std::vector<UserInfo> queryUserInfoList(const std::string& username);
};
#include "grade.h"
std::vector<UserInfo> Grade::queryUserInfoList(const std::string& username)
{
return std::vector<UserInfo>{};
}
6、单例类
规范:把构造函数声明为私有。
原因:基于单例的定义,不允许类型不受控制的实例化。
示例:
头文件DbMgr.h
#pragma once
class DbMgr
{
private:
DbMgr();
public:
static DbMgr& getInstance();
};
源文件DbMgr.cpp
DbMgr::DbMgr() {}
DbMgr& DbMgr::getInstance()
{
static DbMgr s_instance;
return s_instance;
}