白盒测试 [代码规范] [C++] 二

转载自:http://blog.sina.com.cn/s/articlelist_1154237180_1_1.html

代码风格与版式

代码风格的重要性怎么强调都不过分。一段稍长一点的无格式代码基本上就是不可读的。

先来看一下这方面的整体原则:

空行的使用

空行起着分隔程序段落的作用。空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。所以不要舍不得用空行。

  • 在每个类声明之后、每个函数定义结束之后都要加2行空行。
     
  • 在一个函数体内,逻揖上密切相关的语句之间不加空行,其它地方应加空行分隔。

语句与代码行

  • 一行代码只做一件事情,如只定义一个变量,或只写一条语句。这样的代码容易阅读,并且方便于写注释。
     
  • "if"、"for"、"while"、"do"、"try"、"catch" 等语句自占一行,执行语句不得紧跟其后。不论执行语句有多少都要加 "{ }" 。这样可以防止书写 和修改代码时出现失误。

缩进和对齐

  • 程序的分界符 "{" 和 "}" 应独占一行并且位于同一列,同时与引用它们的语句左对齐。
     
  • "{ }" 之内的代码块在 "{" 右边一个制表符(4个半角空格符)处左对齐。如果出现嵌套的 "{ }",则使用缩进对齐。
     
  • 如果一条语句会对其后的多条语句产生影响的话,应该只对该语句做半缩进(2个半角空格符),以突出该语句。

例如:

void
Function(int x)
{
  CSessionLock iLock(mxLock);

    for (初始化; 终止条件; 更新)
    {
        // ...
    }

    try
    {
        // ...
    }
    catch (const exception& err)
    {
        // ...
    }
    catch (...)
    {
        // ...
    }

    // ...
}

 

最大长度

代码行最大长度宜控制在70至80个字符以内。代码行不要过长,否则眼睛看不过来,也不便于打印(2009年更新:随着GUI开发环境和高分宽屏的普及,此规则可以视情况适当放宽)。

 

长行拆分

长表达式要在低优先级操作符处拆分成新行,操作符放在新行之首(以便突出操作符)。拆分出的新行要进行适当的缩进,使排版整齐,语句可读。

例如:

if ((very_longer_variable1 >= very_longer_variable2)
    && (very_longer_variable3 <= very_longer_variable4)
    && (very_longer_variable5 <= very_longer_variable6)
   )
{
    DoSomething();
}

 

空格的使用

  • 关键字之后要留空格。象 "const"、"virtual"、"inline"、"case" 等关键字之后至少要留一个空格,否则无法辨析关键字。象 "if"、"for"、"while"、"catch" 等关键字之后应留一个空格再跟左括号 "(",以突出关键字。
     
  • 函数名之后不要留空格,紧跟左括号 "(" ,以与关键字区别。
     
  • "(" 向后紧跟。而 ")"、","、";" 向前紧跟,紧跟处不留空格。
     
  • "," 之后要留空格,如 Function(x, y, z)。如果 ";" 不是一行的结束符号,其后要留空格,如 for (initialization; condition; update)。
     
  • 赋值操作符、比较操作符、算术操作符、逻辑操作符、位域操作符,如"="、"+=" ">="、"<="、"+"、"*"、"%"、"&&"、"||"、"<<", "^" 等二元操作符的前后应当加空格。
     
  • 一元操作符如 "!"、"~"、"++"、"--"、"&"(地址运算符)等前后不加空格。
     
  • 象"[]"、"."、"->"这类操作符前后不加空格。
     
  • 对于表达式比较长的for、do、while、switch语句和if语句,为了紧凑起见可以适当地去掉一些空格,如for (i=0; i<10; i++)和if ((a<=b) && (c<=d))

例如:

void Func1(int x, int y, int z);    // 良好的风格
void Func1 (int x,int y,int z);     // 不良的风格

// ===========================================================
if (year >= 2000)         // 良好的风格
if(year>=2000)            // 不良的风格

if ((a>=b) && (c<=d))     // 良好的风格
if(a>=b&&c<=d)            // 不良的风格

// ===========================================================
for (i=0; i<10; i++)      // 良好的风格
for(i=0;i<10;i++)         // 不良的风格
for (i = 0; I < 10; i ++) // 过多的空格

// ===========================================================
x = a < b ? a : b;        // 良好的风格
x=a<b?a:b;                // 不好的风格

// ===========================================================
int* x = &y;              // 良好的风格
int * x = & y;            // 不良的风格

// ===========================================================
array[5] = 0;             // 不要写成 array [ 5 ] = 0;
a.Function();             // 不要写成 a . Function();
b->Function();            // 不要写成 b -> Function();

 

修饰符的位置

为便于理解,应当将修饰符 "*" 和 "&" 紧靠数据类型。

例如:

char* name;

int* x;
int  y;    // 为避免y被误解为指针,这里必须分行写。

int* Function(void* p);

参见:变量、常量的风格与版式 -> 指针或引用类型的定义和声明

 

注释

  • 注释的位置应与被描述的代码相邻,可以放在代码的上方或右方,不可放在下方。
  • 边写代码边注释,修改代码同时修改相应的注释,以保证注释与代码的一致性。不再有用的注释要删除。
  • 注释应当准确、易懂,防止注释有二义性。错误的注释不但无益反而有害。
  • 当代码比较长,特别是有多重嵌套时,应当在一些段落的结束处加注释,便于阅读。

与常量的比较

在与宏、常量进行 "==", "!=", ">=", "<=" 等比较运算时,应当将常量写在运算符左边,而变量写在运算符右边。这样可以避免因为偶然写错把比较运算变成了赋值运算的问题。

例如:

if (NULL == p)  // 如果把 "==" 错打成 "=",编译器就会报错
{
    // ...
}

 

为增强代码的可读性而定义的宏

以下预定义宏对程序的编译没有任何影响,只为了增加代码的可读性:

 

说明

NOTE

需要注意的代码

TODO

尚未实现的接口、类、算法等

UNDONE

已取消的接口、类、算法等

FOR_DBG

标记为调试方便而临时增加的代码

OK

仅用于调试的标记

例如:

TODO class CMyClass;
TODO void Function(void);

FOR_DBG cout << "...";

 

类/结构

类是C++中最重要也是使用频率最高的新特性之一。类的版式好坏将极大地影响代码品质。

 

注释头与类声明

与文件一样,每个类应当有一个注释头用来说明该类的各个方面。

类声明换行紧跟在注释头后面,"class" 关键字由行首开始书写,后跟类名称。界定符 "{" 和 "};" 应独占一行,并与 "class" 关键字左 对齐。


class CXXX
{
    // ...
};

对于功能明显的简单类(接口小于10个),也可以使用简单的单行注释头:

//! <简要说明该类所完成的功能>
class CXXX
{
    // ...
};

 

继承

基类直接跟在类名称之后,不换行,访问说明符(public, private, 或protected)不可省略。如:

 

class CXXX : public CAAA, private CBBB
{
    // ...
};

 

以行为为中心

没人喜欢上来就看到一大堆私有数据,大多数用户关心的是类的接口与其提供的服务,而不是其实现。

所以应当将公有的定义和成员放在类声明的最前面,保护的放在中间,而私有的摆在最后。

 

访问说明符

访问说明符(public, private, 或protected)应该独占一行,并与类声明中的‘class’关键字左对齐。

 

类成员的声明版式

对于比较复杂(成员多于20个)的类,其成员必须分类声明。

每类成员的声明由访问说明符(public, private, 或protected)+ 全行注释开始。注释不满全行(80个半角字符)的,由 "/" 字符补齐,最后一个 "/" 字符与注释间要留一个半角空格符。

如果一类声明中有很多组功能不同的成员,还应该用分组注释将其分组。分组注释也要与 "class" 关键字对齐。

每个成员的声明都应该由 "class" 关键字开始向右缩进一个制表符(4个半角空格符),成员之间左对齐。

例如:

class CXXX
{
public:
/// 类型定义
    typedef vector<string> VSTR;

public:
/ 构造、析构、初始化
    CXXX();
    ~CXXX();

public:
/// 公用方法

// [[ 功能组1
    void Function1(void) const;
    long Function2(IN int n);
// ]] 功能组1

// [[ 功能组2
    void Function3(void) const;
    bool Function4(OUT int& n);
// ]] 功能组2

private:
/// 属性
    // ...

private:
/ 禁用的方法
    // 禁止复制
    CXXX(IN const CXXX& rhs);
    CXXX& operator=(IN const CXXX& rhs);
};

 

正确地使用const和mutable

把不改变对象逻辑状态的成员都标记为const成员不仅有利于用户对成员的理解,更可以最大化对象使用方式的灵活性及合理性(比如通过const指针或const引用的形式传递一个对象)。

如果某个属性的改变并不影响该对象逻辑上的状态,而且这个属性需要在const方法中被改变,则该属性应该标记为 "mutable"。

例如:

class CString
{
public:
    //! 查找一个子串,find()不会改变字符串的值所以为const函数
    int find(IN const CString& str) const;
    // ...

private:
    // 最后一次错误值,改动这个值不会影响对象的逻辑状态,
    // 像find()这样的const函数也可能修改这个值
    mutable int m_nLastError;
    // ...
};

也就是说,应当尽量使所有逻辑上只读的操作成为const方法,然后使用mutable解决那些存在逻辑冲突的属性。

 

嵌套的类声明

在相应的逻辑关系确实存在时,类声明可以嵌套。嵌套类可以使用简单的单行注释头:

 

// ...
class CXXX
{
    //! 嵌套类说明
    calss CYYY
    {
        // ...
    };
};

 

初始化列表

应当尽可能通过构造函数的初始化列表来初始化成员和基类。初始化列表至少独占一行,并且与构造函数的定义保持一个制表符(4个半角空格)的缩进。

例如:

CXXX::CXXXX(IN int nA, IN bool bB)
    : m_nA(nA), m_bB(bB)
{
    // ...
};

 

初始化列表的书写顺序应当与对象的构造顺序一致,即:先按照声明顺序写基类初始化,再按照声明顺序写成员初始化。

如果一个成员 "a" 需要使用另一个成员 "b" 来初始化,则 "b" 必须在 "a" 之前声明,否则将会产生运行时错误(有些编译器会给出警告)。

例如:

// ...

class CXXXX : public CAA, public CBB
{
    // ...
    CYY m_iA;
    CZZ m_iB;  // m_iA必须在m_iB之前声明
};


CXXX::CXXXX(IN int nA, IN int nB, IN bool bC)
    : CAA(nA), CBB(nB), m_iA(bC), m_iB(m_iA) // 先基类,后成员,
                                             // 分别按照声明顺序书写
{
    // ...
};

 

内联函数的实现体

定义在类声明之中的函数将自动成为内联函数。但为了使类的声明更为清晰明了,应尽量避免直接在声明中直接定义成员函数的编程风格。鼓励使用 "inline" 关键字将内联函数放在类声明的外部定义。

关于类声明的例子,请参见:类/结构的风格与版式例子

关于类声明的模板,请参见:类声明模板

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值