C++代码风格规范
注释
注释对于保持代码的可读性至关重要
.h文件必须具有doxygen样式的注释: 类,函数和变量
.cpp文件最好有注释来解释变量和算法
例如
/**
* A test class. .....
*/
class Test
{
public :
/**
* Detailed enum description...
*/
enum TEnum {
TVal1 /** describe it... */
} enumVar;
/**
* description of the constructor...
*/
Javadoc_Test();
/**
* a normal member taking two arguments and returning an integer value.
* @param a an integer argument.
* @param s a constant character pointer.
* @see Test()
* @return The test results
*/
int testMe( int a, const char *s);
|
空格与制表符
没有制表符,只能使用“空格”。
(您应该将编辑器设置为在按下Tab键时发出空格。)
局部变量
将函数的变量放在尽可能小的范围内,然后在声明中初始化变量。
C ++允许您在函数中的任何位置声明变量。我们建议您尽可能在局部范围内声明它们,并尽可能接近首次使用。这使读者可以更轻松地找到声明,并查看变量的类型和初始化的对象。特别是,应该使用初始化代替声明和赋值
例1
i = f(); //错误-初始化与声明分开。
int j = g(); //好-声明已初始化。
例2
vector <int> v;
v.push_back(1); //最好使用大括号初始化进行初始化。
v.push_back(2);
vector <int> v = {1,2}; //好-v开始初始化。
变量应在声明它们的地方初始化。
这样可以确保变量在任何时候都是有效的。未初始化的变量错误很难找到。
例如
void getCenter(Ball* ball, int & x, int & y, int & z) {
if (ball != nullprt) {
... ...
}
}
int x, y, z;
getCenter(Ball* ball, &x, &y, &z); // x, y, z might not be initialized
|
作用域较大的变量应使用长名称,作用域较小的变量可以使用短名称
Class
在类中使用声明的指定顺序:public:protected private之前:方法,数据成员(变量)之前的方法,等等。
(您的类定义应从其public:部分开始,然后是protected:部分,然后是private:部分。如果这些部分中的任何一个为空,请忽略它们。)
在每个部分中,声明通常应按以下顺序进行:
使用声明,Typedef和Enums
常量(静态const数据成员)
构造函数
析构
方法,包括静态方法
数据成员(静态const数据成员除外)
功能
有意义的命名
函数名称必须反映其功能。
应避免名称缩写。computeAverage(); //Bad:compAvg();
类成员函数(非静态)
从小写开始。例如getObject(int index);
静态功能
从大写开始。例如float CalculateMean(const Mat&a);
私人成员函数
如果愿意,可以使用_前缀来区分它。例如_getInternalObject(....);
不要在类定义中内联大方法定义:
通常,可以内联定义仅琐碎的或对性能至关重要的且非常简短的方法。有关更多详细信息,请参见内联函数。
变量命名
有意义的
名称应具有描述性;避开缩写。
// Good
int price_count_reader;
int num_errors;
int num_iterations;
// BAD
int n; // Meaningless.
int nerr; // Ambiguous abbreviation.
int n_comp_conns; // Ambiguous abbreviation.
|
除布尔变量和指针外,不应使用隐式测试0。
if (nLines != 0) // NOT: if (nLines)
if (value != 0.0) // NOT: if (value)
|
如果有条件的话应该放在单独的行上,并且必须有{..}
好:
if (isDone) {
doCleanUp();
}
|
坏:
if (isDone) doCleanUp();
// VERY BAD
if (isDone)
doCleanUp();
|
头文件
通常,每个 .cc .c .cpp
文件都应该有一个关联的 .h
文件。
(有一些常见的例外,例如单元测试和.cc
仅包含main()
函数的小 文件 。)
正确使用头文件会对代码的可读性,大小和性能产生巨大影响。
独立的标题
1.标头应具有标头保护,应包括它需要的所有其他标头,并且不应要求定义任何特定的符号。
2.如果在.h文件中声明了模板或内联函数,请在同一文件中对其进行定义。这些构造的定义必须包含在使用它们的每个.cc文件中,否则程序可能无法在某些构造配置中链接。
3. #define Guard
所有头文件都应具有#define防护措施,以防止多次包含。符号名称的格式应为<PROJECT> _ <PATH> _ <FILE> _H_。
或使用:
#pragma once
|
为了保证唯一性,它们应基于项目源代码树中的完整路径。例如,项目foo中的文件foo / src / bar / baz.h应该具有以下防护:
#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_
...
#endif // FOO_BAR_BAZ_H_
|
包括的名称和顺序
项目的所有头文件都应列为项目源目录的后代
例如,
错误:#include“ awesome-project / src / base / logging.h”
好:#include“ base / logging.h”
使用标准顺序以提高可读性并避免隐藏的依赖项:
相关头文件,C库,C ++库,其他库的.h,项目的.h。
例如dir / foo.cc 或dir / foo_test.cc,其主要目的是实现或测试dir2 / foo2.h中的内容,请按以下步骤订购:
dir2 / foo2.h。
C系统文件。
C ++系统文件。
其他库的.h文件。
您项目的.h文件。
例如,awesome-project / src / foo / internal / fooserver.cc中的include可能看起来像这样:
#include "foo/server/fooserver.h"
#include <sys/types.h>
#include <unistd.h>
#include <hash_map>
#include <vector>
#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
|
2.建议的格式化规则
行列
80个字符以内更好。必须少于100个字符
功能长度
1〜2页以内 <100行
参数排序
定义功能时,参数顺序为:输入,然后输出。
参考
通过引用传递的所有参数都必须标记为const。
在函数参数列表中,所有引用必须为const:
void Foo(常量字符串&in,字符串* out);
前缀:
使用前缀可以帮助描述变量的含义 ,以防止产生误解和错误。
m→ member variable. e.g. mIndex.
a → array. e.g. aRectangles
b → bool, e.g. bIsRectangleDetected
s → String. e.g. sName, or std::string strName;
sz → Zero terminated char[] string. e.g. char* szName;
int pxWidth or int imgWidth → width in terms of pixels of an image with int precision
float fWidth → width with float precision
g→ Global variables. gContext
k→ Constant names. const int kDaysInAWeek = 7;
套管
类型应以大写开头。例如:typedef int Line;
变量名应以小写开头。例如 : Line line;
常量应该全部大写。例如MAX_ITERATIONS
类型名称
以大写字母开头,并且有意义
class UrlTable { ...
class UrlTableTester { ...
struct UrlTableProperties { ...
enum UrlTableErrors { ...
|
空白空间
使语句的各个组成部分脱颖而出。增强可读性。
a = (b + c) * d; // NOT: a=(b+c)*d
while ( true ) // NOT: while(true)
{
...
doSomething(a, b, c, d); // NOT: doSomething(a,b,c,d);
for (i = 0; i < 10; i++) { // NOT: for(i=0;i<10;i++){
...
|
声明中的变量可以左对齐。
class A {
AsciiFile* file;
int nPoints;
float x, y;
};
// NOT:
class A {
AsciiFile* file;
int nPoints;
float x, y;
};
|
3. C / C ++语言知识
应当尽量减少使用全局变量
在C ++中,根本没有理由需要使用全局变量。全局函数或文件作用域(静态)变量也是如此。
转换
使用诸如static_cast <float>(double_value)之类的C ++样式转换,或使用大括号初始化进行诸如int64 y = int64 {1} << 42之类的算术类型转换。不要使用诸如int y =(int)x或int y之类的转换格式。 = int(x)(但是在调用类类型的构造函数时可以使用后者)。
C ++引入了与C不同的转换系统,该系统区分了转换操作的类型。
C语言转换的问题在于操作的含糊不清;有时您正在执行转换(例如(int)3.5),有时您正在进行强制转换(例如(int)“ hello”)。括号初始化和C ++强制转换通常可以帮助避免这种歧义。此外,C ++强制类型转换在搜索它们时更为可见。
- 使用括号初始化转换算术类型。这是最安全的方法,因为如果转换会导致信息丢失,则代码将无法编译。语法也很简洁。
- 使用
static_cast
作为相当于C样式转换,当你需要明确的向上转换的指针从一类到它的父,或当你需要明确地将指针从超类的子类。在后一种情况下,必须确保您的对象实际上是子类的实例。 - 使用
const_cast
去除const
限定符(参见 常量)。 - 用于
reinterpret_cast
将指针类型与整数和其他指针类型进行不安全的转换。仅当您知道自己在做什么并且了解别名问题时才使用此功能。