Guía de estilo de Google en C ++. Parte 10

Parte 1. Introducción
...
Parte 9. Comentarios
Parte 10. Formateo
...


Este artículo es una traducción de parte de la guía de estilo de Google en C ++ al ruso.
Artículo original (fork en github), traducción actualizada .

Formateo


El estilo de codificación y formato es arbitrario, pero el proyecto es mucho más fácil de administrar si todos siguen el mismo estilo. Aunque alguien puede estar en desacuerdo con todas las reglas (o usar lo que está acostumbrado), es muy importante que todos sigan las mismas reglas para poder leer y comprender fácilmente el código de otra persona.
Para un formato correcto, creamos un archivo de configuración para emacs .

Longitud de la línea


Es recomendable limitar la longitud de las líneas de código a 80 caracteres.
Esta regla es un poco controvertida, pero la mayor parte del código existente se adhiere a este principio, y también lo apoyamos.

Para los
adherentes a la regla, dicen que no se necesitan líneas más largas, y ajustar constantemente el tamaño de las ventanas es agotador. Además, algunos colocan ventanas con código uno al lado del otro y no pueden aumentar arbitrariamente el ancho de las ventanas. Al mismo tiempo, un ancho de 80 caracteres es un estándar histórico, ¿por qué cambiarlo?

Contra . El

otro lado afirma que las líneas largas pueden mejorar la legibilidad del código. 80 personajes es una reliquia del mainframe de la década de 1960. Las pantallas modernas pueden mostrar líneas más largas.

Un veredicto de

80 caracteres es el máximo.

Una cadena puede exceder un límite de 80 caracteres si:

  • . , URL-, 80 .
  • /, 80 . , .
  • include.
  • using

-ASCII


Los caracteres no ASCII deben usarse tan raramente como sea posible, la codificación debe ser UTF-8.
No tiene que codificar cadenas para mostrar al usuario (incluso en inglés), por lo que los caracteres que no son ASCII deben ser raros. Sin embargo, en algunos casos está permitido incluir tales palabras en el código. Por ejemplo, si el código analiza archivos de datos (con codificación que no está en inglés), es posible incluir palabras delimitadoras nacionales en el código. En un caso más general, el código de prueba de unidad puede contener cadenas nacionales. En estos casos, se debe utilizar la codificación UTF-8, como es entendido por la mayoría de las utilidades (que entienden no solo ASCII).

El maleficio también es válido, especialmente si mejora la legibilidad. Por ejemplo, "\ xEF \ xBB \ xBF" o u8 "\ uFEFF"- Un espacio inextricable de longitud cero en Unicode, y que no debe mostrarse en el texto UTF-8 correcto.

Use el prefijo u8 para que literales como \ uXXXX estén codificados en UTF-8. No lo use para líneas que contengan caracteres no ASCII ya codificados en UTF-8; puede obtener texto torpe si el compilador no reconoce el código fuente como UTF-8.

Evite usar caracteres C ++ 11 char16_t y char32_t ya que son necesarios para líneas no UTF-8. Por las mismas razones, no use wchar_t (excepto cuando trabaje con la API de Windows usando wchar_t ).

Espacios contra pestañas


Use solo espacios para sangría. 2 espacios para una sangría.
Usamos espacios para la sangría. No use pestañas en su código: configure su editor para insertar espacios cuando presione Tab.

Declaraciones de funciones y definiciones


Intente colocar el tipo del valor de retorno, el nombre de la función y sus parámetros en una línea (si todo encaja). Divida una lista de parámetros demasiado larga en líneas, al igual que los argumentos en una llamada de función.

Un ejemplo del diseño correcto de la función:

ReturnType ClassName::FunctionName(Type par_name1, Type par_name2) {
  DoSomething();
  ...
}

Si una línea no es suficiente:

ReturnType ClassName::ReallyLongFunctionName(Type par_name1, Type par_name2,
                                             Type par_name3) {
  DoSomething();
  ...
}

o, si el primer parámetro tampoco encaja:

ReturnType LongClassName::ReallyReallyReallyLongFunctionName(
    Type par_name1,  //  4 
    Type par_name2,
    Type par_name3) {
  DoSomething();  //  2 
  ...
}

Algunas notas

  • Elige buenos nombres para las opciones.
  • Puede omitir el nombre del parámetro si no se usa en la definición de la función.
  • , , . .
  • .
  • .
  • .
  • . .
  • , , .
  • .
  • .
  • — 2 .
  • Al transferir parámetros a otra línea, sangra 4 espacios.

Puede omitir el nombre de los parámetros no utilizados si esto es obvio por el contexto:

class Foo {
 public:
  Foo(const Foo&) = delete;
  Foo& operator=(const Foo&) = delete;
};

Los parámetros no utilizados con contexto no obvio deben comentarse en la definición de la función:

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) {}

Intente utilizar atributos y macros al comienzo de una definición de anuncio o función,
hasta el tipo del valor de retorno:
ABSL_MUST_USE_RESULT bool IsOk();

Lambdas


Formatee los parámetros y el cuerpo de la expresión de la misma manera que una función regular, la lista de variables capturadas es como una lista normal.

Para capturar variables por referencia, no coloque un espacio entre el signo (&) y el nombre de la variable.

int x = 0;
auto x_plus_n = [&x](int n) -> int { return x + n; }

Las lambdas cortas se pueden usar directamente como argumento para una función.

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());

Números de punto flotante


Los números de punto flotante siempre deben estar con un punto decimal y números a cada lado (incluso en el caso de notación exponencial). Tal enfoque mejorará la legibilidad: todos los números de punto flotante estarán en el mismo formato, no lo confundirá con un número entero, y los caracteres E e de notación exponencial no pueden tomarse para dígitos hexadecimales. Recuerde que un número en notación exponencial no es un número entero.

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;

Llamada de función


Escriba la llamada de función completa en una línea o coloque argumentos en una nueva línea. Y la sangría puede estar en el primer argumento o en 4 espacios. Intente minimizar el número de líneas, coloque algunos argumentos en cada línea.

Formato de llamada de función:

bool result = DoSomething(argument1, argument2, argument3);

Si los argumentos no se ajustan a una línea, los dividimos en varias líneas y cada línea subsiguiente se alinea con el primer argumento. No agregue espacios entre paréntesis y argumentos:

bool result = DoSomething(averyveryveryverylongargument1,
                          argument2, argument3);


Está permitido colocar argumentos en varias líneas con una sangría de 4 espacios:
if (...) {
  ...
  ...
  if (...) {
    bool result = DoSomething(
        argument1, argument2,  //  4 
        argument3, argument4);
    ...
  }

Intente colocar varios argumentos por línea, reduciendo el número de líneas por llamada de función (si esto no afecta la legibilidad). Algunas personas piensan que formatear estrictamente en un argumento por línea es más legible y facilita la edición de argumentos. Sin embargo, nos centramos principalmente en los lectores de código (no en la edición), por lo que ofrecemos una serie de enfoques para mejorar la legibilidad.

Si varios argumentos en la misma línea degradan la legibilidad (debido a la complejidad o complejidad de las expresiones), intente crear variables "parlantes" para los argumentos:

int my_heuristic = scores[x] * y + bases[x];
bool result = DoSomething(my_heuristic, x, y, z);

O coloque el argumento complejo en una línea separada y agregue un comentario explicativo:

bool result = DoSomething(scores[x] * y + bases[x],  //  
                          x, y, z);

Si la llamada a la función todavía tiene argumentos que es conveniente colocar en una línea separada, colóquela. Una solución debe basarse en mejorar la legibilidad del código.

Los argumentos a veces forman una estructura. En este caso, formatee los argumentos de acuerdo con la estructura requerida:

//     3x3
my_widget.Transform(x1, x2, x3,
                    y1, y2, y3,
                    z1, z2, z3);


Formatear una lista de inicialización


Formatee la lista de inicialización de la misma manera que una llamada de función.

Si la lista entre paréntesis sigue el nombre (por ejemplo, el nombre de un tipo o variable), formatee {} como si fuera una llamada de función con ese nombre. Incluso si no hay un nombre, considere que solo está vacío.

//      .
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}};

Condiciones


Intente no insertar espacios en el interior de los soportes. Coloque if y else en diferentes líneas.

Hay dos enfoques para las condiciones de formato. Uno permite espacios entre paréntesis y una condición, el otro no.

La opción preferida sin espacios. Otra opción también es válida, pero sea consistente . Si modifica el código existente, use el formato que ya está en el código. Si está escribiendo un código nuevo, use el formato como los archivos ubicados en el mismo directorio o use el formato del proyecto. Si no está seguro, no agregue espacios.

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

Si usa un formato de espacio:

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

Tenga en cuenta que, en cualquier caso, debe haber un espacio entre if y el soporte de apertura. También necesita un espacio entre el corchete de cierre y el corchete (si hay uno).

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

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

Se pueden escribir condiciones cortas en una línea si esto mejora la legibilidad. Use esta opción solo si la línea es corta y la condición no contiene una sección más .

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

No use la versión abreviada si hay otra sección :

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

Por lo general, no se requieren llaves para una condición breve, pero son aceptables. Las condiciones o códigos complicados también se leen mejor con llaves. A menudo se requiere que cualquier caso de estar con paréntesis.

if (condition)
  DoSomething();  //  2 

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

Y si una parte de la condición usa llaves, también emita la segunda con ellas:

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

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


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

Bucles e interruptores


La construcción del interruptor puede usar paréntesis para bloques. Describa las transiciones no triviales entre las opciones. Los corchetes son opcionales para los bucles de expresión única. Un bucle vacío debe usar un cuerpo vacío entre paréntesis o continuar .

Los bloques de cajas en el interruptor pueden ser con llaves, o sin ellas (a su elección). Si se usan paréntesis, use el formato que se describe a continuación.

Se recomienda cambiar a la sección predeterminada en switch . Esto no es necesario cuando se usa una enumeración, y el compilador puede emitir una advertencia si no se procesan todos los valores. Si la sección predeterminada no se debe ejecutar, configúrela como un error. Por ejemplo:

switch (var) {
  case 0: {  //  2 
    ...      //  4 
    break;
  }
  case 1: {
    ...
    break;
  }
  default: {
    assert(false);
  }
}

La transición de una etiqueta a la siguiente debe marcarse con la macro ABSL_FALLTHROUGH_INTENDED; (definido en absl / base / macros.h ).
Coloque ABSL_FALLTHROUGH_INTENDED; en el punto donde será la transición. Una excepción a esta regla son las etiquetas consecutivas sin código, en este caso no es necesario marcar nada.

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;
}

Los soportes son opcionales para bucles de una sola operación.

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");
}

Un bucle vacío debe diseñarse como un par de paréntesis o como continuar sin paréntesis. No use un solo punto y coma.

while (condition) {
  //    false
}
for (int i = 0; i < kSomeNumber; ++i) {}  // .      -   
while (condition) continue;  //  - continue     

while (condition);  //  -     do/while

Punteros y enlaces


Alrededor de '.' y '->' no ponen espacios. El operador de referencia o captura debe estar sin espacios.

Los siguientes son ejemplos de formato adecuado de expresiones con punteros y enlaces:

x = *p;
p = &x;
x = r.y;
x = r->y;

Nota:

  • '.' y '->' se usan sin espacios.
  • Los operadores * o & no están separados por espacios.

Al declarar una variable o argumento, puede colocar '*' tanto en el tipo como en el nombre:

// ,   *, &
char *c;
const std::string &str;

// ,   *, &
char* c;
const std::string& str;

Intente usar un estilo único en el archivo de código; cuando modifique un archivo existente, use el formato utilizado.

Se permite declarar varias variables en una sola expresión. Sin embargo, no use múltiples declaraciones con punteros o enlaces, esto puede ser mal entendido.

//  - 
int x, y;

int x, *y;  //  -      &  *
char * c;  //  -     *
const std::string & str;  //  -     &

Expresiones lógicas


Si la expresión lógica es muy larga (excede el valor típico), utilice un enfoque único para dividir la expresión en líneas.

Por ejemplo, aquí cuando se ajusta el operador AND se encuentra al final de la línea:

if (this_one_thing > this_other_thing &&
    a_third_thing == a_fourth_thing &&
    yet_another && last_one) {
  ...
}

Tenga en cuenta que el código se divide (según el ejemplo) para que && y el operador AND completen la línea. Este estilo se usa con mayor frecuencia con el código de Google, aunque la ubicación de los operadores al comienzo de la línea también es aceptable. Además, puede agregar corchetes adicionales para mejorar la legibilidad. Tenga en cuenta que usar operadores en forma de puntuación (como && y ~ ) es preferible a usar operadores en forma de palabras y y compl .

Valores de retorno


No incluya declaraciones simples de retorno entre paréntesis.

Use paréntesis a cambio de expr; solo si los usó en una expresión de la forma x = expr; .

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

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

Inicializar variables y matrices


Qué usar: = , () o
{} es tu elección.

Puede elegir entre las opciones = ,
() y {} . Los siguientes ejemplos de código son correctos:

int x = 3;
int x(3);
int x{3};
std::string name = "Some Name";
std::string name("Some Name");
std::string name{"Some Name"};

Tenga cuidado al usar la lista de inicialización {...} para un tipo que tiene un constructor con std :: initializer_list .

El compilador preferirá usar el constructor std :: initializer_list cuando haya una lista entre llaves . Tenga en cuenta que las llaves rizadas vacías {} son un caso especial y se llamará al constructor predeterminado (si está disponible). Para usar explícitamente un constructor sin std :: initializer_list, use paréntesis en lugar de llaves.

std::vector<int> v(100, 1);  //    
std::vector<int> v{100, 1};  //   2- : 100  1


Además, la construcción con corchetes prohíbe una serie de transformaciones de tipos enteros (transformaciones con una disminución en la precisión). Y puede obtener errores de compilación.

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

Directivas del pre procesador


El signo # (signo de la directiva del preprocesador) debe estar al comienzo de la línea.

Incluso si la directiva del preprocesador se refiere al código incrustado, las directivas se escriben desde el principio de la línea.

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

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

Formato de clase


Organice las secciones en el siguiente orden: público , protegido y privado . La sangría es un espacio.

El formato básico para la clase se describe a continuación (con la excepción de los comentarios, vea los comentarios sobre la descripción de la clase):

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_;
};

Observaciones:

  • El nombre de la clase base se escribe en la misma línea que el nombre de la clase heredada (por supuesto, teniendo en cuenta el límite de 80 caracteres).
  • Palabras clave del público: , protegidas ,: y privadas: deben tener una sangría de un espacio.
  • Cada una de estas palabras clave debe ir precedida de una línea en blanco (con la excepción de la primera mención). Además, en clases pequeñas, se pueden omitir líneas en blanco.
  • No agregue una línea en blanco después de estas palabras clave.
  • La sección pública debe ser la primera, detrás de ella protegida y al final la sección privada .
  • Vea el Procedimiento de Declaración para construir declaraciones en cada una de estas secciones.

Listas de inicialización de constructor


Las listas de inicialización del constructor pueden estar en una línea o en varias líneas con sangría de 4 espacios.

Los siguientes son los formatos correctos para las listas de inicialización:

//    
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) {}

Formateo de espacios de nombres


El contenido del espacio de nombres está sangrado.

El espacio de nombres no agrega relleno. Por ejemplo:

namespace {

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

}  // namespace

No sangrar en el espacio de nombres:

namespace {

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

}  // namespace

Al declarar espacios de nombres anidados, coloque cada declaración en una línea separada.

namespace foo {
namespace bar {

Desglose horizontal


Use desgloses horizontales según corresponda. Nunca agregue espacios al final de una línea.

Principios generales


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; }  //      
  ...

Agregar espacios delimitadores puede interferir con la fusión de código. Por lo tanto: no agregue espacios de separación al código existente. Puede eliminar espacios si ya ha modificado esta línea. O hágalo como una operación separada (es preferible que nadie trabaje con este código).

Ciclos y condiciones


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

Operadores


//     
x = 0;

//      ,
//   /   .
//          
v = w * x + y / z;
v = w*x + y/z;
v = w * (x + z);

//       
x = -5;
++x;
if (x && !y)
  ...

Patrones y moldes


//       (<  >),
//  <,  >(  
std::vector<std::string> x;
y = static_cast<char*>(x);

//        .       
std::vector<char *> x;

Desglose vertical


Minimiza la división vertical.

Esto es más un principio que una regla: no agregue líneas en blanco sin una necesidad especial. En particular, coloque no más de 1-2 líneas vacías entre las funciones, no inicie la función con una línea vacía, no termine la función con una línea vacía e intente usar menos líneas vacías. Una línea vacía en un bloque de código debería funcionar como un párrafo en una novela: separe visualmente dos ideas.

El principio básico: cuanto más código cabe en una pantalla, más fácil es comprender y rastrear la secuencia de ejecución. Use la cadena vacía únicamente para separar visualmente esta secuencia.

Algunas notas útiles sobre líneas en blanco:

  • Una línea vacía al principio o al final de una función no mejorará la legibilidad.
  • Las líneas vacías en la cadena de bloques if-else pueden mejorar la legibilidad.
  • Una línea vacía frente a la línea de comentarios generalmente ayuda a la legibilidad del código; un nuevo comentario generalmente implica la finalización de un pensamiento antiguo y el comienzo de una nueva idea. Y la línea vacía claramente lo insinúa.

All Articles