Google Style 学习

作者: CapJon | 来源:发表于2014-12-27 22:48 被阅读177次

    头文件

    • 头文件避免多重包含。#ifndef PROJ_PATH_FILE_H_
    • 能用前置声明就不要使用#include
    • 函数的参数顺序:输入参数,输出参数。

    作用域

    • 不要使用using
    • 命名空间不需缩进。
    • .c中可以使用using.h中必须在函数、方法、类内部使用。
    • 非成员函数、静态成员函数、全局函数尽量放到命名空间中。
    • 局部变量声明的时候赋值。在离第一次使用尽可能近的地方声明。
    • 可在while循环中声明变量限制其作用域。
    • 注意有时需在循环外声明变量,比如类的对象,在循环内声明需要循环调用构造函数和析构函数。

    • 构造函数只进行一些非重要内容的初始化,因为如果操作失败会造成对象初始化失败,进入不确定状态。
      可能的话,使用Init()方法集中初始化有意义的数据。
    • 如果类有很多个变量,最好设置默认构造函数,否则编译器可能会生成一个很糟糕的构造函数。
    • 对单个参数的构造函数使用explicit关键字,防止默认强制类型转换。
    • 一般如果没有拷贝构造函数,编译器会自动声明拷贝构造函数,而且是public的。
      如果是单例模式需要禁止拷贝,则可以在private中声明但不定义拷贝构造函数,这样当试图使用它们时编译器将报错。用宏来描述:
    #define DISALLOW_COPY_AND_ASSIGN(TypeName) \\
                TypeName(const TypeName&); \\
                void operator=(const TypeName&)
             private:
            DISALLOW_COPY_AND_ASSIGN(Foo);
    
    • 当只有数据时使用struct,其他一概用classstruct只可以有构造函数,析构函数,Initialize()Reset()Validate()
    • 组合 > 实现继承 > 接口继承 > 私有继承,子类重载的虚函数也要声明virtual关键字。
    • 使用组合(composition)通常比继承更合理。如果需要类继承,使用public继承。
    • 尽量做到is-a继承,在has-a继承时尽量使用composition
    • 数据成员在任何情况下应该都只是私有的。
    • 当定义一个虚函数时,明确声明其为virtual,使得代码阅读者在检查基类的时候易于判断。
      如果类有虚函数,则析构函数最好也声明为virtual
    • 真正需要用到多重实现继承的情况少之又少。
      只在以下情况我们才允许多重继承:最多只有一个基类是非抽象类,其它基类都是以Interface为后缀的纯接口类。接口类类名以Interface为后缀。
    • 为降低复杂性,尽量不重载操作符。
    • 在类中使用特定的声明顺序:public:在 private:之前,成员函数在数据成员(变量)前。顺序:
    • typedefs和枚举
    • 常量->构造函数
    • 析构函数
    • 成员函数, 含静态成员函数
    • 数据成员, 含静态数据成员
    • 将所有数据成员声明为 private, 并根据需要提供相应的存取函数。存取函数一般内联在头文件中。
    • 倾向编写简短, 凝练的函数。如果函数超过 40 行,可以思索一下能不能在不影响程序结构的前提下对其进行分割。

    其他 C++ 特性

    • 所有按引用传递参数必须加上const。函数中非输出参数最好都加上const
    • 在任何可能的地方加上const
    • 如果你想重载一个函数,考虑让函数名包含参数信息,例如,使用 AppendString()AppendInt()而不是Append()
    • 尽可能少的将函数的参数设置默认值。
    • 减少使用变长数组。
    • 允许合理的友元类和友元函数。
    • 类型转换时需要明确指明static_castconst_castreinterpret_castdynamic_cast
    • 只在打印日志的时候使用流。流最大的优势是在输出时不需要关心打印对象的类型。
      这是一个亮点,同时也是一个不足:你很容易用错类型,而编译器不会报警。
      使用流时容易造成的这类错误:
    cout << this;   // Prints the address
     cout << *this;  // Prints the contents
    
    • 对于迭代器和其他模板对象使用前置的自增自减。
    • 使用宏时要非常谨慎,尽量以内联函数、枚举和常量代替之。
      不要在 .h 文件中定义宏。
      在马上要使用时才进行 #define。使用后要立即 #undef
    • 整数用 0,实数用0.0,指针用 NULL,字符(串)用'\\0'
    • 尽可能用sizeof(varname)代替sizeof(type),代码中变量类型改变后可以进行自动更新。

    命名约定

    • 尽可能给出描述性的名称。不要节约行空间,让别人很快理解你的代码更重要。
     int num_errors;                  // Good.
     int num_completed_connections;   // Good
    
    • 类型和变量名一般为名词:如FileOpenernum_errors
      函数名通常是指令性的,如OpenFile()set_num_errors()
      取值函数是个特例,函数名和它要取值的变量同名。
    • 除非该缩写在其它地方都非常普遍,否则不要使用。永远不要用省略字母的缩写。
     int error_count;  // Good
     int error_cnt;    // Bad
    
    • 文件名要全部小写,可以包含下划线(_)或连字符 (-)。可接受的文件命名:my_useful_class.ccmy-useful-class.ccyusefulclass.cc,通常应尽量让文件名更加明确。
    • 类型名称的每个单词首字母均大写,不包含下划线,大驼峰法。
      所有类型命名:类、结构体、类型定义(typedef)、枚举->均使用相同约定。
    • 变量名一律小写,单词之间用下划线连接。类的成员变量以下划线结尾。
      结构体的数据成员可以和普通变量一样,不用像类那样接下划线。
    • 常量名称前加k,如:const int kDaysInAWeek = 7;
    • 常规函数的函数名的每个单词首字母大写,没有下划线。
    • 取值和设值函数要与存取的变量名匹配。
    • int num_entries_;
    • int num_entries() const { return num_entries_; }
    • 枚举内数据的命名应当和常量或宏一致:kEnumName或是ENUM_NAME
    • 宏命名:全大小,下划线。如#define PI_ROUNDED 3.0

    注释

    • 注释要言简意赅, 不要拖沓冗余, 复杂的东西简单化和简单的东西复杂化都是要被鄙视的。
    • 注释固然很重要, 但最好的代码本身应该是自文档化。有意义的类型名和变量名,要远胜过要用注释解释的含糊不清的名字。
    • 在每一个文件开头加入版权公告,然后是文件内容描述。
      每个文件都应该包含以下项, 依次是:
    • 版权声明 (比如,Copyright 2008 Google Inc.)
    • 许可证. 为项目选择合适的许可证版本 (比如,Apache 2.0, BSD, LGPL, GPL)
    • 作者: 标识文件的原始作者.
    • 通常,.h文件要对所声明的类的功能和用法作简单说明,.cc 文件通常包含了更多的实现细节或算法技巧讨论。如果你感觉这些实现细节或算法技巧讨论对于理解 .h 文件有帮助,可以该注释挪到 .h,并在.cc中指出文档在.h
      不要简单的在.h.cc间复制注释,这种偏离了注释的实际意义。
    • 每个类的定义都要附带一份注释,描述类的功能和用法。
    • 对于代码中巧妙的,晦涩的,有趣的,重要的地方加以注释。
    • 比较隐晦的地方要在行尾空两格进行注释。
    • 向函数传入NULL, 布尔值或整数时, 要注释说明含义, 或使用常量让代码望文知意:
     bool success = CalculateSomething(interesting_value,
                                       10,     // Default base value.
                                       false,  // Not the first time we're calling this.
                                       NULL);  // No callback.
    

    或使用常量或描述性变量:

     const int kDefaultBaseValue = 10;
     const bool kFirstTimeCalling = false;
     Callback *null_callback = NULL;
     bool success = CalculateSomething(interesting_value,
                                       kDefaultBaseValue,
                                       kFirstTimeCalling,
                                       null_callback);
    
    • 连续多行输入需要对齐:
    DoSomething();                  // Comment here so the comments line up.
    DoSomethingElseThatIsLonger();  // Comment here so there are two spaces between
                                    // the code and the comment.
    { // One space before comment when opening a new scope is allowed,
      // thus the comment lines up with the following comments and code.
      DoSomethingElse();  // Two spaces before line comments normally.
    }
    
    • 注意永远不要用自然语言翻译代码作为注释。
    • TODO注释
    // TODO(kl@gmail.com): Use a "*" here for concatenation operator.
    // TODO(Zeke) change this to use relations.
    

    如果加 TODO 是为了在 “将来某一天做某事”, 可以附上一个非常明确的时间 “Fix by November 2005”), 或者一个明确的事项 (“Remove this code when all clients can handle XML responses.”)。

    格式

    • 尽量不使用非 ASCII 字符, 使用时必须使用 UTF-8 编码。
    • 返回类型和函数名在同一行, 参数也尽量放在同一行。
      函数看上去像这样:
    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 space indent
            Type par_name2,
            Type par_name3) {
        DoSomething();  // 2 space indent
        ...
    }
    
    • 注意以下几点:
      • 返回值总是和函数名在同一行;
      • 左圆括号总是和函数名在同一行;
      • 函数名和左圆括号间没有空格;
      • 圆括号与参数间没有空格;
      • 左大括号总在最后一个参数同一行的末尾处;
      • 右大括号总是单独位于函数最后一行;
      • 右圆括号和左大括号间总是有一个空格;
      • 函数声明和实现处的所有形参名称必须保持一致;
      • 所有形参应尽可能对齐;
      • 缺省缩进为 2 个空格;
      • 换行后的参数保持 4 个空格的缩进;
    • 如果函数声明成 const, 关键字const 应与最后一个参数位于同一行:
    // Everything in this function signature fits on a single line
    ReturnType FunctionName(Type par) const {
      ...
    }
    
    // This function signature requires multiple lines, but
    // the const keyword is on the line with the last parameter.
    ReturnType ReallyLongFunctionName(Type par1,
                                      Type par2) const {
      ...
    }
    
    • 如果有些参数没有用到, 在函数定义处将参数名注释起来。
    // Comment out unused named parameters in definitions.
    void Circle::Rotate(double /*radians*/) {}
    
    • 函数调用遵循如下形式:
    bool retval = DoSomething(argument1, argument2, argument3);
    

    如果同一行放不下, 可断为多行, 后面每一行都和第一个实参对齐, 左圆括号后和右圆括号前不要留空格:

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

    如果函数参数很多, 出于可读性的考虑可以在每行只放一个参数:

    bool retval = DoSomething(argument1,
                              argument2,
                              argument3,
                              argument4);
    

    如果函数名非常长, 以至于超过 行最大长度, 可以将所有参数独立成行:

    if (...) {
      ...
      ...
      if (...) {
        DoSomethingThatRequiresALongFunctionName(
            very_long_argument1,  // 4 space indent
            argument2,
            argument3,
            argument4);
      }
    
    • 条件语句,如if和左圆括号间都有个空格。右圆括号和左大括号之间也要有个空格。
    • switch语句:
    switch (var) {
      case 0: {  // 2 space indent
        ...      // 4 space indent
        break;
      }
      case 1: {
        ...
        break;
      }
      default: {
        assert(false);
      }
    }
    
    • 空循环体应使用 {}continue, 而不是一个简单的分号。
    while (condition) {
      // Repeat test until it returns false.
    }
    for (int i = 0; i < kSomeNumber; ++i) {}  // Good - empty body.
    while (condition) continue;  // Good - continue indicates no logic.
    Warning
    while (condition);  // Bad - looks like part of do/while loop.
    
    • 指针和引用:句点或箭头前后不要有空格. 指针/地址操作符 (*, &) 之后不能有空格。
    • 逻辑与 (&&) 操作符总位于行尾:
    if (this_one_thing > this_other_thing &&
        a_third_thing == a_fourth_thing &&
        yet_another & last_one) {
      ...
    }
    
    • return 表达式中不要用圆括号包围。
    • 预处理指令不要缩进,从行首开始。
      即使预处理指令位于缩进代码块中, 指令也应从行首开始。
    // Good - directives at beginning of line
      if (lopsided_score) {
    #if DISASTER_PENDING      // Correct -- Starts at beginning of line
        DropEverything();
    #endif
        BackToNormal();
      }
    
    • 访问控制块的声明依次序是 public:, protected:, private:, 每次缩进 1 个空格。
    • 除第一个关键词(一般是public)外, 其他关键词前要空一行。这些关键词后不要保留空行。
    • 名字空间内容不缩进。
    • 垂直留白越少越好。这不仅仅是规则而是原则问题了: 不在万不得已, 不要使用空行,尤其是:
    • 两个函数定义之间的空行不要超过 2 行, 函数体首尾不要留空行, 函数体中也不要随意添加空行。
    • 基本原则是: 同一屏可以显示的代码越多,越容易理解程序的控制流。
    • 当然,过于密集的代码块和过于疏松的代码块同样难看, 取决于你的判断. 但通常是垂直留白越少越好。

    结束语

    • 运用常识和判断力,并保持一致。
    • 风格指南的重点在于提供一个通用的编程规范, 这样大家可以把精力集中在实现内容而不是表现形式上。我们展示了全局的风格规范, 但局部风格也很重要。
    • 如果你在一个文件中新加的代码和原有代码风格相去甚远, 这就破坏了文件本身的整体美观, 也影响阅读, 所以要尽量避免。

    参考阅读

    Google C++ 风格指南 - 中文版
    Google 开源项目风格指南

    相关文章

      网友评论

        本文标题:Google Style 学习

        本文链接:https://www.haomeiwen.com/subject/bfoatttx.html