C/C++编码规范_地大信工学院版

C / C++ 编程规范

2018-12-24   Version 1.0 by Liu Penghua @ SYSU & Yao Yao @ CUG

1. 代码可读性

1.1. 命名规范

1.1.1. 函数命名规则(驼峰命名法)

普通函数的函数名由若干个单词组成,第一个单词全部小写,第二个单词开始首字母大写。

bool getMeanValue(...);	
int csvToShp(...);	
double** computeUrbanConversionMatrix(...);

a. 如果是 inline 类型的函数,在函数名前加下划线 _

inline int _getCuberInterpolationValue(…);

b. 如果是 static 类型的函数,函数名第一个单词首字母大写

static int OpenFiles(…);

1.1.2. 变量命名规则(匈牙利命名法)

  • 原则 1:禁止使用简单英文单词命名变量,如 min,max,left,right,会造成和某些库保留变量名的冲突
  • 原则 2: 变量作用域(w/m/无) + 指针/数组(p/pp/ppp/无) + 变量类型(c/u/n/f/d/s/v 等) + 变量名称
  • 原则 3:迭代变量允许但不推荐 i,j,k,m,n。但尽量使用有意义的迭代变量名称,如: _nFilesIdx, _nUser_inMember_i

表 1 变量作用域命名规则

变量作用域前缀例子意义
全局变量wwnValue全局 int 型变量
静态变量S (大写)SnValue静态 int 型变量
类变量mmnValue类 int 型变量
普通变量不需要nValueint 型变量
临时变量__nValue临时 int 型变量

表 2 指针/数组命名规则

指针/数组前缀例子意义
一维ppdValues普通一维 double 型数组
二维ppppdValues普通二维 double 型数组
三维ppppppdValues普通三维 double 型数组
非指针dValue普通 double 型对象

表 3 变量类型命名规则

变量类型前缀例子意义
charccValue
unsigned char, byteuuValue
short, unsigned shortnnValue
int, unsigned intnnValue
long, long longllValue
floatffValue
doubleddValue
boolbbValue
char*, strings / str / szstrValue
list, vectorvvValues链表/容器
filein/out/fiinFile / outFile / fiOutput输入输出文件
map, hashmapmapKeyValues映射表/哈希表

1.2. 整洁、对齐

  • 错误范例
int main(int argc, char* argv[])
{
    int nRow=5;//数组行数
    int nCol=5;//数组列数
    int nValue=3;//数组元素值
    //创建二维指针,初始化数组数据
    double **ppdData = new *double[nRow];
    for(int i=0;i<nRow;i++){ppdData[i]=new double[nCol];memset(ppdData[i],0,sizeof(double)*nCol);}
    return 0;
}
  • 修改

运算符与变量之间空格;;,等表示间隔的符号后面空格;语句之间换行;代码片段之间空格;注释中,中英文之间尽量空格;注释对齐;缩进对齐。

int main(int argc, char* argv[])
{
    int nRow = 5;		//数组行数
    int nCol = 5;		//数组列数
    int nValue = 3;		//数组元素数
  
    // 创建二维指针,初始化数组数据
    double **ppdData = new *double[nRow];
    for(int i = 0; i < nRow; i++){
        ppdData[i] = new double[nCol];
        // 数组元素赋值为 0
        memset(ppdData[i], 0, sizeof(double)*nCol);
    }
  
    return 0;
}

1.3. 编写注释

  • 精简:没有太大用处的不写,能从代码直接看出含义的不写,类中的getset方法不写
// The first String is student's name
// The Second Integer is student's score
std::map<std::string, int> mapScoreMap;
// Student' name -> Student's score
std::map<std::string, int> mapScoreMap;
  • 不因为写了注释就给变量随意取名;
  • 通过注释来记录当前解决方案的算法过程,使得读者易于理解;
  • 注释用于提醒一些特殊情况;
标记用法
TODO待做
FIXME待修复
HACH粗糙的解决方案
XXX危险!这里有重要问题
//TODO: 设置源和目的地块适宜性(需要外部读入,是否自己输入这部分还要修正)
//TODO: 添加外部地块适宜性输入代码,pSuitability需要和mvStoreNames匹配
//TODO: load attributes from table
for (int i=0; i<mvOrigLandParcels.size(); i++){
            mvOrigLandParcels[i].pdSuitability = new double[mnStoreCount];  
            for (int j=0; j<mnStoreCount; j++)
                mvOrigLandParcels[i].pdSuitability[j] = 0.0f;
        }
  • 添加测试用例来说明;
//...
// Example: add(1, 2), return 3
int add(int x, int y) {
return x + y;
}
  • 在很复杂的函数调用中对每个参数标上名字
int a = 1;
int b = 2;
int num = add(/* x = */ a, /* y = */ b);
  • 对代码的注释应放在其上方或右方(对单条语句的注释)相邻位置,不可放在下面;
  • 函数头部应进行注释,列出:函数的目的/功能、输入参数、输出参数、返回值、调用关系(函数、表)等;
/*************************************************
Description: 对一个数组中的元素求和
Parameters: 
	pnData : 数据指针,int *
	n	   : 数组长度, int
Return: // 数组元素之和,int
*************************************************/
int arrSum(int *pnData, int n);
  • 说明性文件(如头文件.h文件、.inc文件、.def文件、编译说明文件.cfg等)头部应进行注释,注释必须列出:版权说明、版本号、生成日期、作者、内容、功能、与其它文件的关系、修改日志等,头文件的注释中还应有函数功能简要说明;
/************************************************************
FileName: SparseAutoencoder.h
Author: liuphhhh
Version : 1.0
Date: 2018-03-20
Description: // 模块描述
Function List: // 主要函数及其功能
1. -------
History: // 历史修改记录
<author> <time> <version > <desc>
liuphhhh 18/03/20 1.0 build this moudle
***********************************************************/
  • 注释量一般不低于20%。(要求不低于30%

1.4. 拆分长表达式

拆分前

if (split(line, ',')[0].strip() == "CUG" && split(line, ':')[1].strip() == "GIS")

拆分后

sList = split(line, ':');
if (sList[0].strip == "CUG" && sList[1].strip() == "GIS")

1.5. 减小变量的作用域

  • 尽量不使用全局变量
  • 作用域越小,越容易定位到变量所有使用的代码区间,这在动态类型的语言(例如Python,Javascript)中尤为重要。

1.6. 任务分解,函数抽取

  • 大问题拆分成小问题。首先应该明确一个函数的高层次目标,然后对于不是直接为了这个目标工作的代码,抽取出来放到独立的函数中。
  • 并非函数抽取的越多越好,如果抽取过多,在阅读代码的时候可能需要不断跳来跳去。
  • 函数抽取也用于减小代码的冗余。
// 问题:
// 找出10个向量中最相似(余弦值最大)的两个向量之间的余弦距离
// 假设所有向量存储在一个二维数组中,每一行为一个向量(的转置),每一个向量为5维,那么存储向量的数组应当为10 × 5 大小


double findMostSimilarVectors(double **ppdVec, int nVecNum = 10, int nDim = 5)
{
      // 初始化向量之间余弦值的最小值
      double _dCos = -1;
      for(int i = 0; i < nVecNum-1; i ++)
          for(int j = i+1; j < nVecNum; j ++)
          {
              // 计算两个向量之间的余弦值;
              double _tmpDis = calCosine(ppdVec[i], ppdVec[j]);
  			   if(_tmpDis > _dCos) _dCos = _tmpDis;
          }
  
     return _dCos;
}

double calCosine(double *pdV1, double *pdV2)
{
    //...  
}

1.7. No过度设计

编码过程会有很多变化,过度设计的内容到最后往往是无用的。确保代码的rubust,模块分解需要适度,太零碎的模块不易于整合。

例如以上计算两个向量之间最小的余弦距离,没必要再另外写一个查找最小值的函数。


1.8. Coding之前整理逻辑,书写伪代码

先用自然语言书写代码逻辑,也就是伪代码,然后再写代码,这样代码逻辑会更清晰。

Algorithm Bagging.

**Input:**
D: a set of *d* training tuples
k: the number of models in the ensemble
a classification learning scheme(decision tree algorithm, naive Bayesian, etc.)
**Output:** the ensemble--a composite model M*
**Method:**
(1) **for** i = 1 to *k* **do** //create *k* models
(2)      create bootstrap sample *D_i*, by sampling D with replacement
(3)      use *D_i* and the learning scheme to derive a model *M_i*
(4) **endfor**

2. 指针内存管理

2.1. 一维指针

为防止内存泄漏造成程序不稳定,在类和函数中声明的指针一定要先置 NULLnullptr;每次使用前先判断是否为 NULL,若需要更新先 deletenew;并且在类的析构函数中或函数尾部对该指针 delete 并置 NULLnullptr 处理。

// 先判断是否为空
if (mpVals != NULL)
{
    // delete 并且置空
    delete[]mpVals;
    mpVals = NULL;
}
// 再 new
mpVals = new double[5];
memset(mpVals, 0, sizeof(double) * 5);

2.2. 二维指针

int nRows = 10, nCols = 20;
// 创建二维指针
double** _pp = new double*[nRows];
for (int i = 0; i < nRows; i++)
{
    _pp[i] = new double[nCols];
    memset(_pp[i], 0, nCols * sizeof(double));
}

// 删除
for (int i = 0; i < nRows; i++)
{
    delete[]_pp[i];
    _pp[i] = NULL;
}
delete[]_pp;
_pp = NULL;

3. 其他

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值