【C/C++】Google 出品的代码规范(Google C++ Style Guide)

翻译整理自 Google C++ Style GuideRefRefGoogle C++ Style Guide : 官方原版C++ Coding Style : 不错的表格汇总Google C++ 风格指南: 很完整的中文翻译一张图总结Google C++编程规范(Google C++ Style Guide): 非常帮的一张图,可以用于概览,回忆...
摘要由CSDN通过智能技术生成

文章目录

I. 开篇, 一图胜千言

x

此图来自 一张图总结Google C++编程规范(Google C++ Style Guide)

具体细节,娓娓道来。

II. 分门别类, 娓娓道来

本章来自于官方文档的摘录. Google C++ Style Guide

Principles 原则

  • Style rules should pull their weight.
  • Optimize for the reader, not the writer
  • Be consistent with existing code
  • Be consistent with the broader C++ community when appropriate
  • Avoid surprising or dangerous constructs
  • Avoid constructs that our average C++ programmer would find tricky or hard to maintain
  • Be mindful of our scale
  • Concede to optimization when necessary

Header Files 头文件

Self-contained Headers

Header files should be self-contained (compile on their own) and end in .h. Non-header files that are meant for inclusion should end in .inc and be used sparingly.

The #define Guard #define 保护

  • 按文件夹顺序: <PROJECT>_<PATH>_<FILE>_H_.

. For example, the file foo/src/bar/baz.h in project foo should have the following guard:

#ifndef FOO_BAR_BAZ_H_
#define FOO_BAR_BAZ_H_

...

#endif  // FOO_BAR_BAZ_H_

Forward Declarations 前置声明

A “forward declaration” is a declaration of a class, function, or template without an associated definition.

  • 尽量不用, 需要什么就 #include

Inline Functions 内联函数

  • 小于 10 行的简单直接的小程序, 才写成内联函数

Names and Order of Includes

In dir/foo.cc or dir/foo_test.cc, whose main purpose is to implement or test the stuff in dir2/foo2.h, order your includes as follows:

  1. dir2/foo2.h.
  2. A blank line
  3. C system files.
  4. C++ system files.
  5. A blank line
  6. Other libraries’ .h files.
  7. Your project’s .h files.

For example, the includes in google-awesome-project/src/foo/internal/fooserver.cc might look like this:

#include "foo/server/fooserver.h"

#include <sys/types.h>
#include <unistd.h>
#include <vector>

#include "base/basictypes.h"
#include "base/commandlineflags.h"
#include "foo/server/bar.h"
  • system-specific code needs conditional includes. 后置
#include "foo/public/fooserver.h"

#include "base/port.h"  // For LANG_CXX11.

#ifdef LANG_CXX11
#include <initializer_list>
#endif  // LANG_CXX11

Scope 域

Namespaces 命名空间

  • 所有逻辑代码一律放在 namespace 里
// In the .h file
namespace mynamespace {

// All declarations are within the namespace scope.
// Notice the lack of indentation.
class MyClass {
 public:
  ...
  void Foo();
};

}  // namespace mynamespace
// In the .cc file
namespace mynamespace {

// Definition of functions is within scope of the namespace.
void MyClass::Foo() {
  ...
}

}  // namespace mynamespace
  • 一律禁止使用 using namespace XXX, 如 using namespace std
  • 可以使用 using, 如 using std::string
  • 不使用 inline namespace.

Nonmember, Static Member, and Global Functions

  • 放在单独的 namespace 中

Local Variables

  • Place a function’s variables in the narrowest scope possible, and initialize variables in the declaration.
int i;
i = f();      // Bad -- initialization separate from declaration.
int j = g();  // Good -- declaration has initialization.


std::vector<int> v;
v.push_back(1);  // Prefer initializing using brace initialization.
v.push_back(2);
std::vector<int> v = {
   1, 2};  // Good -- v starts initialized.


//Variables needed for if, while and for statements should normally be declared within those statements, 
// so that such variables are confined to those scopes. E.g.:

while (const char* p = strchr(str, '/')) str = p + 1;

// There is one caveat: if the variable is an object, its constructor is invoked every time 
// it enters scope and is created, and its destructor is invoked every time it goes out of scope.

// Inefficient implementation:
for (int i = 0; i < 1000000; ++i) {
   
  Foo f;  // My ctor and dtor get called 1000000 times each.
  f.DoSomething(i);
}
// It may be more efficient to declare such a variable used in a loop outside that loop:

Foo f;  // My ctor and dtor get called once each.
for (int i = 0; i < 1000000; ++i) {
   
  f.DoSomething(i);
}

Static and Global Variables

Classes 类

Doing Work in Constructors

  • 绝不能调用虚函数 Avoid virtual method calls in constructors
  • 决不能调用可能失败的操作 avoid initialization that can fail if you can’t signal an error.
  • 可以把上面的操作添加到一个 Init() 函数中, 注意 Init 不要滥用
  • 推荐使用 factory function, 具体理由参见 TotW #42 类似这样
// foo.h
class Foo {
   
 public:
  // Factory method: creates and returns a Foo.
  // May return null on failure.
  static std::unique_ptr<Foo> Create();

  // Foo is not copyable.
  Foo(const Foo&) = delete;
  Foo& operator=(const Foo&) = delete;

 private:
  // Clients can't invoke the constructor directly.
  Foo();
};

// foo.c
std::unique_ptr<Foo> Foo::Create() {
   
  // Note that since Foo's constructor is private, we have to use new.
  return absl::WrapUnique(new Foo());
}

Implicit Conversions

  • 单参数构造函数必须使用 explicit
  • 特例: copy and move constructors should not be explicit since they do not perform type conversion
  • 特例: Implicit conversions can sometimes be necessary and appropriate for types that are designed to transparently wrap other types. In that case, contact your project leads to request a waiver of this rule.
  • Constructors that cannot be called with a single argument may omit explicit.
  • Constructors that take a single std::initializer_list parameter should also omit explicit, in order to support copy-initialization (e.g. MyType m = {1, 2}?.

Copyable and Movable Types

  • A movable type is one that can be initialized and assigned from temporaries.
  • A copyable type is one that can be initialized or assigned from any other object of the same type (so is also movable by definition), with the stipulation that the value of the source does not change.
  • int and string are examples of movable types that are also copyable.
  • std::unique_ptr<int> is an example of a movable but not copyable type (since the value of the source std::unique_ptr<int> must be modified during assignment to the destination)
  • A class’s public API should make explicit whether the class is copyable, move-only, or neither copyable nor movable. Support copying and/or moving if these operations are clear and meaningful for your type.
  • 怎么限制, 以以下的代码形式
class Copyable {
   
 public:
  Copyable(const Copyable& rhs) = default;
  Copyable& operator=(const Copyable& rhs) = default;

  // The implicit move operations are suppressed by the declarations above.
};

class MoveOnly {
   
 public:
  MoveOnly(MoveOnly&& rhs);
  MoveOnly& operator=(MoveOnly&& rhs);

  // The copy operations are implicitly deleted, but you can
  // spell that out explicitly if you want:
  MoveOnly(const MoveOnly&) = delete;
  MoveOnly& operator=(const MoveOnly&) = delete;
};

class NotCopyableOrMovable {
   
 public:
  // Not copyable or movable
  NotCopyableOrMovable(const NotCopyableOrMovable&) = delete;
  NotCopyableOrMovable& operator=(const NotCopyableOrMovable&)
      = delete;

  // The move operations are implicitly disabled, but you can
  // spell that out explicitly if you want:
  NotCopyableOrMovable(NotCopyableOrMovable&&) = delete;
  NotCopyableOrMovable& operator=(NotCopyableOrMovable&&)
      = delete;
};

简写版, 在类的private中添加空的拷贝构造函数和赋值操作,并且只有声明,不进行定义

// the old way
#define DISALLOW_COPY_AND_ASSIGN(Type) \
    Type(const Type&);                 \
    void operator = (const Type&)

class Foo
{
public:
    Foo(int f);
    ~Foo();
private:
    DISALLOW_COPY_AND_ASSIGN(Foo);
};

Structs vs. Classes

  • Struct 只用来保存数据组合, 不能带有任何函数
  • 其他所有情况都用 class
  • 特例 : For consistency with STL, you can use struct instead of class for functors and traits.

Inheritance

  • 组合比继承更好 Composition is often more appropriate than inheritance.
  • is-a 情况下可以使用继承. Try to restrict use of inheritance to the “is-a” case: Bar subclasses Foo if it can reasonably be said that Bar “is a kind of” Foo.
  • 必须使用继承时, 确保是 public 继承. When using inheritance, make it public.
  • data 始终是private 的, 可以被子类访问的 method 用 protected : Limit the use of protected to those member functions that might need to be accessed from subclasses
  • 重定义派生的虚函数时,在派生类中明确声明其为virtual : Explicitly annotate overrides of virtual functions or virtual destructors with exactly one of an override or (less frequently) final specifier. Do not use virtual when declaring an override. Rationale: A function or destructor marked override or final that is not an override of a base class virtual function will not compile, and this helps catch common errors. The specifiers serve as documentation; if no specifier is present, the reader has to check all ancestors of the class in question to determine if the function or destructor is virtual or not.
  • Multiple inheritance is permitted, but multiple implementation inheritance is strongly discouraged. 可以考虑使用接口, 纯接口必须以Interface为后缀, 如 RunnableInterface

Operator Overloading

  • Overload operators judiciously. Do not create user-defined literals.
  • Define overloaded operators only if their meaning is obvious, unsurprising, and consistent with the corresponding built-in operators. For example, use | as a bitwise- or logical-or, not as a shell-style pipe.
  • prefer to define ==, =, and <<, rather than Equals(), CopyFrom(), and PrintTo().
  • Do not overload &&, ||, , (comma), or unary &. Do not overload operator"", i.e. do not introduce user-defined literals.
  • don’t define operator overloads just because other libraries expect them. For example, if your type doesn’t have a natural ordering, but you want to store it in a std::set, use a custom comparator rather than overloading <.

Access Control

  • Make classes’ data members private, unless they are static const

Declaration Order

  • Group similar declarations together, placing public parts earlier.
  • A class definition should usually start with a public: section, followed by protected:, then private:. Omit sections that would be empty.
  • Within each section, generally prefer grouping similar kinds of declarations together, and generally prefer the following order:
    • types (including typedef, using, and nested structs and classes),
    • constants,
    • factory functions,
    • constructors,
    • assignment operators,
    • destructor,
    • all other methods,
    • data members.
  • Do not put large method definitions inline in the class definition. Usually, only trivial or performance-critical, and very short, methods may be defined inline.

Functions 函数

Output Parameters

  • The output of a C++ function is naturally provided via a return value and sometimes via output parameters.
  • Prefer using return values instead of output parameters since they improve readability and oftentimes provide the same or better performance.
  • If output-only parameters are used they should appear after input parameters.
  • Input parameters are usually values or const references, while output and input/output parameters will be pointers to non-const.
  • When ordering function parameters, put all input-only parameters before any output parameters.
  • Parameters that are both input and output (often classes/structs) muddy the waters, and, as always, consistency with related functions may require you to bend the rule.

Write Short Functions

  • Prefer small and focused functions.
  • 一般而言, 超过 40 行的代码就需要考虑拆分了.
  • Even if your long function works perfectly now, someone modifying it in a few months may add new behavior. This could result in bugs that are hard to find. Keep
  • 19
    点赞
  • 80
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值