第1部分。简介...第9部分。注释第10部分。格式...
本文是C ++中Google样式指南的一部分翻译成俄语的内容。原始文章(github上的fork),更新了翻译。格式化
编码和格式设置样式是任意的,但是如果每个人都遵循相同的样式,则该项目更易于管理。尽管某人可能不同意所有规则(或使用习惯的规则),但每个人都遵循相同的规则以轻松阅读和理解他人的代码非常重要。为了正确格式化,我们为emacs创建了一个设置文件。线长
建议将代码行的长度限制为80个字符。该规则有点争议,但是现有的大部分代码都遵循此原则,我们也支持它。对于遵守该规则的人,他们说不需要更长的行,并且不断调整窗户的尺寸很麻烦。另外,一些带有代码的窗口彼此相邻放置,并且不能任意增加窗口的宽度。同时,宽度为80个字符是历史标准,为什么要更改它呢?..反对另一方声称,长行可以提高代码的可读性。80个字符是1960年代大型机的遗物。现代屏幕可能会显示更长的线条。的判决80个字符是最大值。在以下情况下,字符串可以超过80个字符的限制:- . , URL-, 80 .
- /, 80 . , .
- include.
- using
-ASCII
非ASCII字符应尽可能少地使用,编码应为UTF-8。您不必对字符串进行硬编码即可显示给用户(甚至是英文),因此非ASCII字符应该很少见。但是,在某些情况下,可以在代码中包含此类单词。例如,如果代码解析数据文件(使用非英语编码),则可以在代码中包含国家分隔符。在更一般的情况下,单元测试代码可能包含国家字符串。在这些情况下,应使用UTF-8编码,因为大多数实用程序都可以理解它(不仅了解ASCII)。十六进制也是有效的,尤其是在提高可读性的情况下。例如,“ \ xEF \ xBB \ xBF”或u8“ \ uFEFF”-Unicode中不可分割的零长度空格,不应在正确的UTF-8文本中显示。使用u8前缀,以便像\ uXXXX 这样的文字都以UTF-8编码。不要将其用于包含已用UTF-8编码的非ASCII字符的行-如果编译器无法将源代码识别为UTF-8,则可能会显示笨拙的文本。避免使用C ++ 11 char16_t和char32_t字符,因为 非UTF-8线需要它们。出于同样的原因,不要使用wchar_t的(与使用Windows API时,除了wchar_t的)。空格与制表符
仅将空格用于缩进。1个缩进2个空格。我们使用空格进行缩进。不要在代码中使用制表符-将编辑器配置为在按Tab时插入空格。函数声明和定义
尝试将返回值的类型,函数的名称及其参数放在一行上(如果一切合适)。将参数列表过长地分成几行,就像函数调用中的参数一样。正确的功能设计示例:ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
DoSomething();
...
}
如果一行不够用:ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
Type par_name3) {
DoSomething();
...
}
或者,如果第一个参数也不适合:ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
Type par_name1,
Type par_name2,
Type par_name3) {
DoSomething();
...
}
一些注意事项:- 选择好名字作为选择。
- 如果函数定义中未使用参数名称,则可以省略。
- , , . .
- .
- .
- .
- . .
- , , .
- .
- .
- — 2 .
- 将参数传输到另一行时,请缩进4个空格。
如果从上下文中可以明显看出,您可以省略未使用参数的名称:class Foo {
public:
Foo(const Foo&) = delete;
Foo& operator=(const Foo&) = delete;
};
具有非显而易见上下文的未使用参数应在函数定义中注释掉:class Shape {
public:
virtual void Rotate(double radians) = 0;
};
class Circle : public Shape {
public:
void Rotate(double radians) override;
};
void Circle::Rotate(double ) {}
void Circle::Rotate(double) {}
尝试在广告或函数定义的开头使用属性和宏,直到返回值的类型:ABSL_MUST_USE_RESULT bool IsOk();
Lambdas
以与常规函数相同的方式设置表达式的参数和主体的格式,捕获变量的列表类似于普通列表。要通过引用捕获变量,请不要在与号(&)和变量名称之间放置空格。int x = 0;
auto x_plus_n = [&x](int n) -> int { return x + n; }
短lambda可以直接用作函数的参数。std::set<int> blacklist = {7, 8, 9};
std::vector<int> digits = {3, 9, 1, 8, 4, 7, 1};
digits.erase(std::remove_if(digits.begin(), digits.end(), [&blacklist](int i) {
return blacklist.find(i) != blacklist.end();
}),
digits.end());
浮点数字
浮点数应始终带有小数点,并且在小数点的任何一侧(即使是指数表示法)也应如此。这种方法将提高可读性:所有浮点数都将采用相同的格式,您不会将其与整数混淆,并且十六进制数字不能采用指数符号的字符E e。请记住,指数表示的数字不是整数。float f = 1.f;
long double ld = -.5L;
double d = 1248e6;
float f = 1.0f;
float f2 = 1;
long double ld = -0.5L;
double d = 1248.0e6;
函数调用
在一行中编写整个函数调用,或在新行中放置参数。缩进可以是第一个参数,也可以是4个空格。尽量减少行数,在每行上放置一些参数。函数调用格式:bool result = DoSomething(argument1, argument2, argument3);
如果参数不适合一行,则将它们分成几行,随后的每一行都与第一个参数对齐。不要在括号和参数之间添加空格:bool result = DoSomething(averyveryveryverylongargument1,
argument2, argument3);
允许在多行中以4个空格的缩进量放置参数:if (...) {
...
...
if (...) {
bool result = DoSomething(
argument1, argument2,
argument3, argument4);
...
}
尝试每行放置几个参数,以减少每个函数调用的行数(如果这不会影响可读性)。有人认为,严格按照每行一个参数的格式进行设置更具可读性,并且使参数的编辑更加容易。但是,我们主要关注代码读取器(而不是编辑器),因此我们提供了许多提高可读性的方法。如果同一行上的多个参数削弱了可读性(由于表达式的复杂性),请尝试为这些参数创建“通话”变量:int my_heuristic = scores[x] * y + bases[x];
bool result = DoSomething(my_heuristic, x, y, z);
或将复杂的参数放在单独的行上并添加解释性注释:bool result = DoSomething(scores[x] * y + bases[x],
x, y, z);
如果函数调用仍带有需要放在单独一行上的参数,则将其放置。解决方案应基于提高代码的可读性。争论有时形成一个结构。在这种情况下,请根据所需的结构设置参数的格式:
my_widget.Transform(x1, x2, x3,
y1, y2, y3,
z1, z2, z3);
格式化初始化列表
以与函数调用相同的方式格式化初始化列表。如果方括号中的列表紧随名称(例如,类型或变量的名称),则将{}格式设置为好像是具有该名称的函数调用一样。即使没有名称,也要考虑是只有空。
return {foo, bar};
functioncall({foo, bar});
std::pair<int, int> p{foo, bar};
SomeFunction(
{"assume a zero-length name before {"},
some_other_function_parameter);
SomeType variable{
some, other, values,
{"assume a zero-length name before {"},
SomeOtherType{
"Very long string requiring the surrounding breaks.",
some, other values},
SomeOtherType{"Slightly shorter string",
some, other, values}};
SomeType variable{
"This is too long to fit all in one line"};
MyType m = {
superlongvariablename1,
superlongvariablename2,
{short, interior, list},
{interiorwrappinglist,
interiorwrappinglist2}};
条件
尽量不要在括号内插入空格。将if和else放在不同的行上。有两种格式化条件的方法。一个允许在方括号和条件之间留有空格,另一个不允许。首选选项,不带空格。另一种选择也是有效的,但要一致。如果您修改现有代码,请使用代码中已经存在的格式。如果要编写新代码,请使用相同目录中的文件之类的格式,或使用项目格式。如果不确定,请勿添加空格。if (condition) {
...
} else if (...) {
...
} else {
...
}
如果使用空格格式:if ( condition ) {
...
} else {
...
}
请注意,无论如何,if和左括号之间必须有一个空格。在右括号和大括号之间也需要有一个空格(如果有)。if(condition) {
if (condition){
if(condition){
if (condition) {
如果可以提高可读性,则可以在一行上写短条件。仅当行短且条件不包含else节时才使用此选项。if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();
如果有其他部分,请不要使用简化版本:
if (x) DoThis();
else DoThat();
通常情况下,短时不需要括号,但可以接受。使用花括号也可以更好地读取复杂的条件或代码。它通常所需的任何如果是用括号。if (condition)
DoSomething();
if (condition) {
DoSomething();
}
如果条件的一部分使用花括号,则还使用花括号:
if (condition) {
foo;
} else
bar;
if (condition)
foo;
else {
bar;
}
if (condition) {
foo;
} else {
bar;
}
回路和开关
switch构造可以对块使用括号。描述选项之间的非平凡过渡。括号对于单个表达式循环是可选的。空循环应在方括号中使用空主体或继续。的情况下,块的开关可以是用大括号,或没有它们(你选择的)。如果使用括号,请使用以下格式。建议切换到switch中的默认部分。使用枚举时这不是必需的,如果未处理所有值,编译器可能会发出警告。如果不应执行默认部分,则将其配置为错误。例如:switch (var) {
case 0: {
...
break;
}
case 1: {
...
break;
}
default: {
assert(false);
}
}
从一个标签到下一个标签的过渡应使用宏ABSL_FALLTHROUGH_INTENDED进行标记;(在absl / base / macros.h中定义)。放置ABSL_FALLTHROUGH_INTENDED; 在过渡点。此规则的一个例外是没有代码的连续标签,在这种情况下,无需标记任何内容。switch (x) {
case 41:
case 43:
if (dont_be_picky) {
ABSL_FALLTHROUGH_INTENDED;
} else {
CloseButNoCigar();
break;
}
case 42:
DoSomethingSpecial();
ABSL_FALLTHROUGH_INTENDED;
default:
DoSomethingGeneric();
break;
}
括号对于单操作循环是可选的。for (int i = 0; i < kSomeNumber; ++i)
printf("I love you\n");
for (int i = 0; i < kSomeNumber; ++i) {
printf("I take it back\n");
}
空循环应设置为一对方括号,或设置为不带方括号的继续样式。不要使用单个分号。while (condition) {
}
for (int i = 0; i < kSomeNumber; ++i) {}
while (condition) continue;
while (condition);
指针和链接
在“。”周围 和'->'不放置空格。取消引用或捕获运算符必须没有空格。以下是使用指针和链接对表达式进行正确格式化的示例:x = *p;
p = &x;
x = r.y;
x = r->y;
注意:- '。' 和'->'不能使用空格。
- *或&运算符不能用空格分隔。
在声明变量或参数时,可以在类型和名称上都放置“ *”:
char *c;
const std::string &str;
char* c;
const std::string& str;
尝试在代码文件中使用一种样式;修改现有文件时,请使用所使用的格式。允许在一个表达式中声明多个变量。但是,请勿将多个声明与指针或链接一起使用-这可能会被误解。
int x, y;
int x, *y;
char * c;
const std::string & str;
逻辑表达式
如果逻辑表达式很长(超过典型值),请使用一种方法将表达式分成几行。例如,在这里,包装AND运算符位于行的末尾:if (this_one_thing > this_other_thing &&
a_third_thing == a_fourth_thing &&
yet_another && last_one) {
...
}
请注意,该代码已拆分(根据示例),以便&&和AND运算符可完成该行。尽管操作符在行首的位置也是可以接受的,但该样式通常用于Google代码。另外,您可以添加其他括号以提高可读性。请注意,使用标点符号形式的运算符(例如&&和〜)比使用单词and和compl形式的运算符更可取。返回值
不要将简单的return语句括在括号中。在return expr中使用括号;仅当您以x = expr形式的表达式使用它们时;。return result;
return (some_long_condition &&
another_condition);
return (value);
return(result);
初始化变量和数组
使用方法:=,()或{}是您的选择。您可以在=,()和{}之间进行选择。下面的代码示例是正确的:int x = 3;
int x(3);
int x{3};
std::string name = "Some Name";
std::string name("Some Name");
std::string name{"Some Name"};
将初始化列表{...}用于具有带有std :: initializer_list构造函数的类型时,请务必小心。当括号中有列表时,编译器将更喜欢使用std :: initializer_list构造函数。请注意,空花括号{}是一种特殊情况,默认构造函数将被调用(如果可用)。要显式使用不带std :: initializer_list的构造函数,请使用括号而不是大括号。std::vector<int> v(100, 1);
std::vector<int> v{100, 1};
同样,使用大括号构造会禁止一系列整数类型的转换(精度降低的转换)。而且您会得到编译错误。int pi(3.14);
int pi{3.14};
预处理程序指令
符号#(预处理器指令的符号)应位于该行的开头。即使预处理器指令引用了嵌入式代码,指令也是从行的开头开始编写的。
if (lopsided_score) {
#if DISASTER_PENDING
DropEverything();
# if NOTIFY
NotifyClient();
# endif
#endif
BackToNormal();
}
if (lopsided_score) {
#if DISASTER_PENDING
DropEverything();
#endif
BackToNormal();
}
类格式
按以下顺序排列各节:public,protected和private。缩进是一个空格。该类的基本格式如下所述(注释除外,请参见对类描述的注释):class MyClass : public OtherClass {
public:
MyClass();
explicit MyClass(int var);
~MyClass() {}
void SomeFunction();
void SomeFunctionThatDoesNothing() {
}
void set_some_var(int var) { some_var_ = var; }
int some_var() const { return some_var_; }
private:
bool SomeInternalFunction();
int some_var_;
int some_other_var_;
};
备注:- 基类的名称与继承的类的名称写在同一行(当然要考虑到80个字符的限制)。
- 关键字public:,protected 、:和private:应该缩进一个空格。
- 这些关键字中的每个关键字都必须以空白行开头(第一次提及时除外)。同样,在小类中,可以省略空白行。
- 在这些关键字之后不要添加空行。
- 公共部分应该是第一个,在其后面受保护,最后是私有部分。
- 请参阅声明过程中的每个部分中的构建声明。
构造函数初始化列表
构造函数初始化列表可以位于一行中,也可以位于具有4个空格缩进的几行中。以下是初始化列表的正确格式:
MyClass::MyClass(int var) : some_var_(var) {
DoSomething();
}
MyClass::MyClass(int var)
: some_var_(var), some_other_var_(var + 1) {
DoSomething();
}
MyClass::MyClass(int var)
: some_var_(var),
some_other_var_(var + 1) {
DoSomething();
}
MyClass::MyClass(int var)
: some_var_(var) {}
格式化命名空间
命名空间内容缩进。名称空间不添加填充。例如:namespace {
void foo() {
...
}
}
不要缩进名称空间:namespace {
void foo() {
...
}
}
声明嵌套名称空间时,请将每个声明放在单独的行上。namespace foo {
namespace bar {
水平分解
适当使用水平分解。切勿在行尾添加空格。一般原则
void f(bool b) {
...
int i = 0;
int x[] = { 0 };
int x[] = {0};
class Foo : public Bar {
public:
Foo(int b) : Bar(), baz_(b) {}
void Reset() { baz_ = 0; }
...
添加定界符空格可能会干扰代码合并。因此:不要在现有代码中添加空格。如果您已经修改了此行,则可以删除空格。或者将其作为单独的操作进行(最好没有人使用此代码)。周期和条件
if (b) {
} else {
}
while (test) {}
switch (i) {
for (int i = 0; i < 5; ++i) {
switch ( i ) {
if ( test ) {
for ( int i = 0; i < 5; ++i ) {
for ( ; i < 5 ; ++i) {
...
for (auto x : counts) {
...
}
switch (i) {
case 1:
...
case 2: break;
经营者
x = 0;
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);
x = -5;
++x;
if (x && !y)
...
模式和演员
std::vector<std::string> x;
y = static_cast<char*>(x);
std::vector<char *> x;
垂直击穿
最小化垂直分割。这比规则更重要:不要在没有特殊需要的情况下添加空白行。特别是,在函数之间放置不超过1-2个空行,不要以空行开始函数,不要以空行结束函数,并尽量减少使用空行。代码块中的空行应像小说中的一段一样工作:在视觉上将两个想法分开。基本原理:在一个屏幕上放置的代码越多,就越容易理解和跟踪执行顺序。仅使用空字符串在视觉上分隔此序列。有关空行的一些有用的注释:- 函数开头或结尾的空行不会提高可读性。
- if-else区块链中的空行可以提高可读性。
- 注释行前面的空行通常有助于代码的可读性-新注释通常涉及旧思想的完成和新思想的开始。空行清楚地暗示了这一点。