C ++中的Google样式指南。第10部分

第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_tchar32_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,  //  4 
    Type par_name2,
    Type par_name3) {
  DoSomething();  //  2 
  ...
}

一些注意事项:

  • 选择好名字作为选择。
  • 如果函数定义中未使用参数名称,则可以省略。
  • , , . .
  • .
  • .
  • .
  • . .
  • , , .
  • .
  • .
  • — 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 /*radians*/) {}

//   -  -     ,
//    .
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,  //  4 
        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);

如果函数调用仍带有需要放在单独一行上的参数,则将其放置。解决方案应基于提高代码的可读性。

争论有时形成一个结构。在这种情况下,请根据所需的结构设置参数的格式:

//     3x3
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 = {  // Here, you could also break before {.
    superlongvariablename1,
    superlongvariablename2,
    {short, interior, list},
    {interiorwrappinglist,
     interiorwrappinglist2}};

条件


尽量不要在括号内插入空格。ifelse放在不同的行上。

有两种格式化条件的方法。一个允许在方括号和条件之间留有空格,另一个不允许。

首选选项,不带空格。另一种选择也是有效的,但要一致如果您修改现有代码,请使用代码中已经存在的格式。如果要编写新代码,请使用相同目录中的文件之类的格式,或使用项目格式。如果不确定,请勿添加空格。

if (condition) {  //    
  ...  //  2 
} else if (...) {  // 'else'      
  ...
} else {
  ...
}

如果使用空格格式:

if ( condition ) {  //   
  ...  //  2 
} else {  // 'else'      
  ...
}

请注意,无论如何,if和左括号之间必须有一个空格在右括号和大括号之间也需要有一个空格(如果有)。

if(condition) {   //  -    'if'
if (condition){   //  -    {
if(condition){    //  

if (condition) {  //   -     'if'   {

如果可以提高可读性,则可以在一行上写短条件。仅当行短且条件不包含else节时才使用此选项

if (x == kFoo) return new Foo();
if (x == kBar) return new Bar();

如果有其他部分,请不要使用简化版本

//  -    ,   'else'
if (x) DoThis();
else DoThat();

通常情况下,短时不需要括号,但可以接受。使用花括号也可以更好地读取复杂的条件或代码。它通常所需的任何如果是用括号。

if (condition)
  DoSomething();  //  2 

if (condition) {
  DoSomething();  //  2 
}

如果条件的一部分使用花括号,则还使用花括号:

//  -    'if',  'else' - 
if (condition) {
  foo;
} else
  bar;

//  -    'else',  'if' - 
if (condition)
  foo;
else {
  bar;
}


//  -     'if'   'else'
if (condition) {
  foo;
} else {
  bar;
}

回路和开关


switch构造可以对块使用括号。描述选项之间的非平凡过渡。括号对于单个表达式循环是可选的。空循环应在方括号中使用空主体或继续

情况下,开关可以是用大括号,或没有它们(你选择的)。如果使用括号,请使用以下格式。

建议切换到switch中的默认部分。使用枚举时这不是必需的,如果未处理所有值,编译器可能会发出警告。如果不应执行默认部分,则将其配置为错误。例如:

switch (var) {
  case 0: {  //  2 
    ...      //  4 
    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) {
  //    false
}
for (int i = 0; i < kSomeNumber; ++i) {}  // .      -   
while (condition) continue;  //  - continue     

while (condition);  //  -     do/while

指针和链接


在“。”周围 和'->'不放置空格。取消引用或捕获运算符必须没有空格。

以下是使用指针和链接对表达式进行正确格式化的示例:

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代码。另外,您可以添加其他括号以提高可读性。请注意,使用标点符号形式的运算符(例如&&)比使用单词andcompl形式的运算符更可取

返回值


不要将简单的return语句在括号中。

return expr中使用括号仅当您以x = expr形式的表达式使用它们时

return result;                  //   -  
//  - .    
return (some_long_condition &&
        another_condition);

return (value);                // . ,      var = (value);
return(result);                // . return -   !

初始化变量和数组


使用方法:=()
{}是您的选择。

您可以在=
(){}之间进行选择下面的代码示例是正确的:

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};  //   2- : 100  1


同样,使用大括号构造会禁止一系列整数类型的转换(精度降低的转换)。而且您会得到编译错误。

int pi(3.14);  // : pi == 3
int pi{3.14};  //  : "" 

预处理程序指令


符号(预处理器指令的符号)应位于该行的开头。

即使预处理器指令引用了嵌入式代码,指令也是从行的开头开始编写的。

//  -    
  if (lopsided_score) {
#if DISASTER_PENDING      //  -    
    DropEverything();
# if NOTIFY               //   # - ,   
    NotifyClient();
# endif
#endif
    BackToNormal();
  }

//  -   
  if (lopsided_score) {
    #if DISASTER_PENDING  // ! "#if"     
    DropEverything();
    #endif                // !     "#endif"
    BackToNormal();
  }

类格式


按以下顺序排列各节:publicprotectedprivate缩进是一个空格。

该类的基本格式如下所述(注释除外,请参见对类描述的注释):

class MyClass : public OtherClass {
 public:      //  1 
  MyClass();  //  2-  
  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),             //  4 
      some_other_var_(var + 1) {  //   
  DoSomething();
}

//     ,       
MyClass::MyClass(int var)
    : some_var_(var) {}

格式化命名空间


命名空间内容缩进。

名称空间不添加填充。例如:

namespace {

void foo() {  // .   
  ...
}

}  // namespace

不要缩进名称空间:

namespace {

  // .   ,   
  void foo() {
    ...
  }

}  // namespace

声明嵌套名称空间时,请将每个声明放在单独的行上。

namespace foo {
namespace bar {

水平分解


适当使用水平分解。切勿在行尾添加空格。

一般原则


void f(bool b) {  //       
  ...
int i = 0;  //       
//            .
//    ,      
int x[] = { 0 };
int x[] = {0};

//        
class Foo : public Bar {
 public:
  //  inline-  
  //     (  )
  Foo(int b) : Bar(), baz_(b) {}  //    
  void Reset() { baz_ = 0; }  //      
  ...

添加定界符空格可能会干扰代码合并。因此:不要在现有代码中添加空格。如果您已经修改了此行,则可以删除空格。或者将其作为单独的操作进行(最好没有人使用此代码)。

周期和条件


if (b) {          //        
} else {          //   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  
    ...
  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区块链中的空行可以提高可读性。
  • 注释行前面的空行通常有助于代码的可读性-新注释通常涉及旧思想的完成和新思想的开始。空行清楚地暗示了这一点。

All Articles