C++:代码规范

一。文件和目录

在C/C++编程中,应该使用如下的文件名后缀:
C++的头文件:.hpp(系统自动生成时允许.h,比如VC)
C++的Inline函数实现头文件:_I.hpp
C++的源文件:.cpp
C的头文件:.h
C的源文件:.c
Perl编程中:
Perl程序的源文件:.pl
C#编程中:
C#程序的源文件:.cs
Shell编程中:
Shell脚本的源文件:.sh

使用统一而且通用的文件名后缀可以增加可读性和移植性,还能简化诸如make等工具的依赖规则。
使用hpp和cpp是因为这是VC可以支持的比较通用的文件名后缀,也是UNIX可以接受的。使用.hpp而不是.h是为了区分C++和C程序的头文件。(建议自己生成的头文件使用.hpp。编译器生成的头文件允许使用.h)
在C++中不使用.i而是用_I.hpp是因为大多数编译器不认识.i这种后缀,或者.i文件对于编译器有特殊的用途,会引起冲突。使用_I.hpp而不是I.hpp是因为前者更加清楚一点。
扩展名都是用小写是因为有的平台是大小写区分的,统一用小写有利 于增加平台间的移植性。

二。 排版

程序块采用缩进风格编写,每次用4各空格键。
在程序适当的地方加入空行
说明
在以下地方应该是用空行分开:

  1.   函数之间应该用空行分开;
    
  2.   一组局部变量声明和代码之间用空行分开;
    
  3.   用空行将代码按照逻辑片断划分;
    
  4.   除非函数非常简单(如只有一两条语句),否则函数返回语句和其他语句用空行分开;
    
  5.   每个类声明之后应该加入空格同其他代码分开;
    

原因
空行得体(不过多也不过少)将使程序的布局更加清晰。空行不会浪费内存,虽然打印含有空行的程序是会多消耗一些纸张,但是值得。
函数中较长的变量声明分成多行编写
一行只写一条语句

if、for、do、while等语句自占一行,其后无论执行语句多长,都应该使用{}
说明
例外的情况是:如果存在多个if…else…语句并列,则else可以和if放在同一行。
如下的写法不合规范:
if ( 0 == a) return NULL;
C/C++应该这样写:
if ( 0 == a)
{
return value;
}
Java可以这样写
if ( 0== a) {
return value;
}

如下的写法不合规范:
if ( 0 == a) return NULL;
C/C++应该这样写:
if ( 0 == a)
{
return value;
}
Java可以这样写
if ( 0== a) {
return value;
}

类内普通成员、类内静态数据成员、全局变量、文件作用域静态变量分别使用i_或者m_,c_,g_,f_;它们的含义分别是:instance scope,class scope;global,file scope

三。注释

文件头注释
说明
使用C/C++或者Java编程的时候,在每个源程序文件的头部进行如下的注释:
/**

  • File: WinMain.cpp
  • Purpose: The main program of HelloWorld. It calls init functions and
  • contains the message loop.
    
  • @author Tom
  • @version 1.0 2002/10/28
  • Copyright © 2000, 2002, Yashentech, Inc.

*/
注意文件头注释为javadoc的文档注释类型,下面的函数、类和结构的注释也是使用javadoc的文档注释类型。
如果注释的内容超过一行,缩进也是四个空格。

使用C/C++或者Java编程的时候,每一个Public类型函数开头要有如下的注释,其他函数建议有(缺省构造函数、拷贝构造函数和析构函数可以不注释,成员属性的Set和Get方法可以不注释):
/**

  • :<对函数的简单描述>
  • @param <函数参数> <对函数参数的简单描述>
  • @return <函数的返回值> <对返回值的简单描述>
  • @throws <抛出的异常><抛出的异常的描述>
  • @see <参见的内容>
  • @deprecated <删除的内容>
    */
    如果函数没有返回值,则这样书写:@return void,如果函数没有参数或者异常抛出,@param和@throws可以省略。@see和@deprecated标签只在必要的时候使用就可以了。

类注释
说明
使用C/C++或者Java编程的时候,每一个类(或者sturct、union)开头要有如下的注释:
/**

  • :<对这个类的简单描述>
  • @author <作者的名字>
  • @version <版本信息>
  • @see <参见的内容>
  • @deprecated <删除的内容>
    */
    其中class-type说明这是个类,还是struct,还是union、interface等,@see和@deprecated标签只在必要的时候使用就可以了。

四。判断语句

对于判断语句,遵循以下的写法(假设变量名为flag)

  1.   如果flag是布尔型变量,则如此书写:
    

if (flag)
if (!flag)
2. 如果flag是浮点型,避免使用等于或者不等于判断:
如这样的写法不好:
if (flag == 0)
而应该使用类似这种大于等于或者小于等于进行判断:
if (flag >= -0.000001 && flag <= 0.000001)
3. 如果flag是指针,避免它和0直接进行比较:
如这样的写法不对:
if ( flag == 0)

if ( flag != 0)
而应该写成:
If ( NULL == flag)

If ( NULL != flag)
4. 如果flag是整数,避免下面的写法:
if (flag)

if (!flag)
而应该写成
if (0 == flag)

if (0 != flag)

五。const约束

尽量使用const约束
示例
const char* ClassA::GetData(int const* pValue) const
{
return m_pData;
}
第一个const 表示返回的指针所指向的内容在外部不可修改,第二个表示传入的参数指针所指向的内容在函数内不可修改,第三个表示该函数不会造成类成员的变化;
原因
使用const约束能提高安全性,减少调用者和被调用对象的耦合程度,降低维护的复杂程度。

六。类成员初始化
在分配存储时初始化成员
说明
对于简单数据类型的成员,在分配存储空间时,而不是构造函数初内赋值。
示例
ClassA::ClassA()
:m_pData(NULL),
,m_nCount(0)
{
//…
}
而不是
ClassA::ClassA()
{
m_pData = NULL;
m_nCount = 0;
//…
}
原因
在给对象分配内存时即填充值,而不是构造函数执行时再次执行赋值语句。也有利于初始化代码的检查,避免因成员未初始化造成未知结果。养成在头文件添加申明时随之添加初始化的习惯。

七。资源
建议尽量使用现有的免费资源
www.sourceforge.net。
C++单元测试建议使用CppUnit;
C++多线程编程建议使用ZThread;
C++日志编程建议使用Log4Cpp;
C++数据库接口编程建议使用Odbc,ADO,SqlApi,OTL;
Java数据库接口编程建议使用Jdbc,hibernate等。

1 命名常量应当使用全大写字母,单词之间使用下划线连接
Common practice in the C++ development community. 通常情况下,常量的使用应该控制在最小限度内,在很多情况下可以把值变为方法:

int getMaxIterations() // NOT: MAX_ITERATIONS = 25

{

return 25;

}

这样既清晰阅读,又确保了这个接口对于类来说值是统一的。
2
名函数时一定要使用动词并且起始字母小写。
getName(), computeTotalWidth()
3
命名空间应该使用小写
model::analyzer, io::iomanager, common::math::geometry
C++社区常用规范。
4
命名模板类型应该使用一个单一的大写字母。
template … template<class C, class D> …
C++社区常用规范。 这样使得模板名称与其他使用的名称清晰的独立出来
5
简略词必须不使用全大写
exportHtmlSource(); // NOT: exportHTMLSource();openDvdPlayer(); // NOT: openDVDPlayer();
使用全大写的缩略词会导致冲突,像dVD,hTML这类的同样也不利于阅读,而且当略缩词后面连接了一个单词后,会减少后面单词的可读性。
6
全局变量应当使用操作符::
::mainWindow.open(), ::applicationContext.getName()
通常应该避免使用全局变量,考虑单例模式的对象来代替。
7
私有类变量应该使用下划线字尾.
class SomeClass { private: int length_; }
除了变量的名称和类型,作用域是一个变量最重要的属性。 its name and its type, the scopeof a variable is its most important feature. 通过下划线来区分局部变量和类的私有变量是很方便的事情。因为类变量具有比函数变量更大的作用域因此应当值得程序员的注意。同时还使得当变量出现问题时,更方便的解决。 void setDepth (int depth) { depth_ = depth; }这种方案的一个问题就是应当使用下划线做前缀还是后缀,这两种方法都被广泛的应用,但后缀更加值得推荐一下,因为我们觉得这样使得单词看起来更舒服。值得注意的是变量作用域标识的问题已经有很长时间了,这个指南中这条规则被更多的应用了。
8
通用的变量名应该与类型名一致
void setTopic(Topic* topic) // NOT: void setTopic(Topic* value) // NOT: void setTopic(Topic* aTopic) // NOT: void setTopic(Topic* t)void connect(Database* database) // NOT: void connect(Database* db) // NOT: void connect (Database* oracleDB)
通过减少不同变量名等方法来减少代码的复杂度,同样使得通过变量名猜测变量类型变得容易。如果某些情况下无法满足这条指南,非通用的变量会有一个特殊的角色,那么这些变量可以把角色和类型联合起来: Point startingPoint, centerPoint; Name loginName;
9 变量的作用域长,使用长变量名,变量作用域短,使用短变量名。

10
对象名是一种暗示,应该避免再次出现在方法中
line.getLength(); // NOT: line.getLineLength();
在类定义时函数看起来似乎正常,但在上例中可以看的很清楚,多余了。

函数命名
11
当一个属性被允许修改,那么必须要通过使用get/set函数
employee.getName();employee.setName(name);

12
可以使用compute命名需要在函数内进行计算的函数
valueSet->computeAverage();matrix->computeInverse()
13

可以使用find命名需要在函数内进行查找的函数
vertex.findNearestVertex(); matrix.findMinElement();
14
initialize 可以用来命名一个对象或强模板的建立。
printer.initializeFontSet();
15
. 代表着可视化组件的变量,应该具有组件类型的后缀名。
mainWindow, propertiesDialog, widthScale, loginText,leftScrollbar, mainForm, fileMenu, minLabel, exitButton, yesToggle etc.
增加可读性,可以直观的看出变量类型和对象资源。
16
枚举常量应具有一个共同的前缀类型名
enum Color { COLOR_RED, COLOR_GREEN, COLOR_BLUE };
这样命名增加了额外信息,哪里可以找到这些变量、哪几种常量是一起的以及代表了什么样的内容。一种替代方式是保持通过类型进行引用Color::RED, Airline::AIR_FRANCE注意枚举类型的变量名应该是单数形式enum Color {…}.一个复数的名字尽管看起来没有什么区别,但在使用时看起来非常愚蠢。
include

17
头文件必须要包含一个头文件守护
#ifndef COM_COMPANY_MODULE_CLASSNAME_H#define COM_COMPANY_MODULE_CLASSNAME_H :#endif // COM_COMPANY_MODULE_CLASSNAME_H
这样的语法结构用来避免编译错误。这样命名统一了头文件出现在源文件中的位置,避免了名字冲突。
18
Include语句应当按组排列,按照在系统中的位置进行排序,低等级的在最前方声明,每个组之间保留一个空行。
#include #include #include <qt/qbutton.h>#include <qt/qtextfield.h> #include “com/company/ui/PropertiesDialog.h”#include “com/company/ui/MainWindow.h”
除了可以给读者一个清晰的包含文件的结构,还可以直观的看出使用了哪些模块。包含文件的路径永远不要使用绝对路径,编译器的指令才应用来指定包含文件的根目录。

19
类一定要按照public、protected和private的顺序来声明。每种类型必须要精确的组织出来,不应当有混合情况。(All sections must be identified explicitly. Not applicable sections should be left out. )

顺序就是最常用的放到第一位,这样当需要使用这个类时,可以在读到代码段protected或private时停止。
20
类型转换必须要表示出来,不要依靠隐藏的类型转换
floatValue = static_cast(intValue); // NOT: floatValue = intValue;
这样程序员表面了他意识到了不同类型的转换,而需要进行。
21
类变量不应当被声明为public

public变量会破坏C++的信息隐藏和抽象。使用私有变量和访问函数来代替。有一种例外情况是当类像C中的结构体一样时,只包含数据。值得注意结构体在C++中只是为了与C语言的兼容性,因此应当尽量不使用struct而是用类。
22
C++ pointers and references should have their reference symbol next to the type rather than to the name.
float* x; // NOT: float *x; int& y; // NOT: int &y;
The pointer-ness or reference-ness of a variable is a property of the type rather than the name. C-programmers often use the alternative approach, while in C++ it has become more common to follow this recommendation.
23 无限循环中应当使用while(true)

24复杂的条件判断必须避免,使用临时的布尔变量保存

25
浮点常数应当带有小数点和至少一位小数。
double total = 0.0; // NOT: double total = 0;double speed = 3.0e8; // NOT: double speed = 3e8;double sum;:sum = (a + b) * 10.0;
强调了整数与浮点数的区别,同样,像最后的例子里,他表达了变量(sum)的类型,这仅仅从(a+b)中是无法看出来的。

  1. Names representing types must be in mixed case starting with upper case
    代表类型的名字必须首字母大写并且其它字母大小写混合
    例如:Line, SavingsAccount
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章