翻译:Chromium C++ style guide

翻译Chromium C++ style guide

Chromium follows the Google C++ Style Guide unless an exception is listed below.(这里只是跟Google代码风格的一点点小区别而已)

Blink code in third_party/WebKit uses Blink style

翻译的原因:
1、练习英文
2、作为一个职场小新想感受Google的C++代码风格

翻译的不是很好,欢迎指正

现代C++的新特性

尽管Chromium采用了c++ 语言和标准库的新版本,但 c++ 的一些特性仍然是禁止的。使用的新特性应该与谷歌样式中允许的相近,但有时可能有所不同。我们单独在页面中记录了chromium的C++ 的使用方式以及最新的状态

命名方式

“Chromium”是项目的名称,而不是产品的名称,不应该出现在代码、变量名、API名等中。而是使用“Chrome”进行命名。

仅测试代码

仅用于测试的函数应该被限制为带有ForTesting后缀的仅用于测试的函数。这在预提交时(presubmit time)进行检查,以确保这些函数只被测试文件调用。

代码风格

  1. Put * and & by the type(类型) rather than the variable name(变量名).
  2. 在类的声明中,一组override的函数放一块,并且使用一个标记来进行命名(In class declarations, group function overrides together within each access control section, with one labeled group per parent class.)
  3. Prefer (foo == 0) to (0 == foo).(假如少了一个等号,前者就变成了赋值了)

未命名空间

  1. .cc文件的本地项应该包装在未命名的名称空间中。
  2. 虽然在c++中,默认情况下有些对象、函数等的作用范围只在本文件中,但并不是所有的都是。
  3. 并且在Linux上的共享对象可以export所有符号,因此未命名的名称空间(将这些符号限制在编译单元中)提高了函数调用成本,并减小了入口点表的大小。

导出符号数据(symbols)

用_EXPORT宏名进行注释的符号数据可以导出(在共享库/DLL外部可见),其中是正在构建的组件的名称。
类注释应该放在类名前面:

class FOO_EXPORT Foo {
  void Bar();
  void Baz();
  // ...
};

函数注释应该放在返回类型前面:

class FooSingleton {
  FOO_EXPORT Foo& GetFoo();
  FOO_EXPORT Foo& SetFooForTesting(Foo* foo);
  void SetFoo(Foo* foo);  // Not exported.
};

多重继承

chromium代码允许多重继承和虚继承。但是,超出Google代码规范的可继承的接口,我们不需要类中有接口后缀。

内联函数

  1. 简单的访存器应该是唯一的内联函数。这些应该用snake_case()来命名。
  2. 虚函数不应该这样来声明。

日志记录

日志在需要更新(checking in)的时候把大部分的日志删除。通常来说不应该添加日志语句,除非添加一些临时的日志来跟踪一些特定的bug,并且需要做一些计划从用户的机器里面收集日志。
少数情况下,一些需要在代码库里面存留一段时间的日志,我们最好使用DVLOG(1)而不是使用其他的日志方法。这样可以避免可执行文件过大,而在调试的时候可以在命令行选择性开启一些参数:

  1. –v=n 设置全局的日志等级为n(默认是0)。所有的日志使用等级都是小于或者等于全局的等级才会被打印出来。
  2. –vmodule=mod=n[,mod=n,…] 覆盖模块mod的全局日志等级。为mod提供字符串foo将影响所有名为foo的文件。当提供像bar/baz这样的通配符时,会影响所有带有bar/baz完整路径名的文件。

特定平台的代码

用#ifdef来区分特定的代码块,使用build/build_config.h和Chromium构建配置中定义的宏,而不是由其他特定的编译器或者构建环境定义的宏(例如WIN32)
通常用于特定平台的#include是放在常规的#include下面。例子如下:

  #include "foo/foo.h"

  #include <stdint.h>
  #include <algorithm>

  #include "base/strings/utf_string_conversions.h"
  #include "chrome/common/render_messages.h"

  #if defined(OS_WIN)
  #include <windows.h>
  #include "base/win/com_init_util.h"
  #elif defined(OS_POSIX)
  #include "base/posix/global_descriptors.h"
  #endif

类型

1、对对象和分配大小、对象数量、数组和指针偏移量、向量索引等使用size_t。在处理STL api时,这可以防止强制转换,如果在整个代码库中一致地执行这个原则,则可以最小化其他地方的强制转换。

2、有时候类有很好的理由使用size_t以外的类型来实现这些概念之一,例如作为存储空间优化。在这些情况下,继续在面向公共的函数声明中使用size_t,并继续在内部使用无符号类型(例如uint32_t)。

3、当您知道转换是安全的时,请遵循谷歌c++转换约定来转换算术类型。当您需要检查源值是否位于目标类型的范围中时,使用checked_cast(在base/numerics/safe_conversions.h)。如果您希望夹紧超出范围的值(clamp out-of-range values),请使用saturated_cast。CheckedNumeric是一种比较优秀的办法,在许多情况下可以执行安全的运算。

4、在跨网络或进程边界传递值时,为了安全起见,请使用明确大小的类型,因为发送端和接收端可能没有使用相同大小字节的int和size_t。我们需要做的是避免让这些大小的类型渗透到相关层的api中去。

5、不要使用std:: wstring。而是使用base::string16或base::FilePath。

对象所属关系和调用关系

当函数需要将原始指针或智能指针作为参数时,请使用以下约定。

这里我们将参数类型称为T,变量命名为t

  1. 如果函数没有修改t的所有权,则将参数声明为T*。通常情况在调用期间,用户需要确保变量t在活动状态。但是:在极少数情况下(例如在unique_ptr<>s的容器上使用带STL算法的lambdas表达式),可能会强制将参数声明为const std::unique_ptr&。用户只有在需要时才这样做。
  2. 如果函数接受函数对象数量的单一,则将参数声明为std::unique_ptr。
  3. 有时候函数接受引用计数对象的引用(takes a ref on a refcounted object),则将参数声明为scoped_refptr。调用者可以决定是希望转移所有权(通过在传递t时调用std::move(t)),还是保留它的引用(通过直接传递t)。
  4. 总之,函数永远不应该拥有作为原始指针传递的参数的所有权,并且很少需要通过const 引用 传递智能指针。

返回值的约定类似,但有一个重要区别:

  1. 只有当调用者没有指针的所有权的时候才返回原始指针。
  2. 当函数实现中交接指针的所有权的时候,使用std::unique_ptr or scoped_refptr传值
  3. 区别:当函数实现保持所有权的时候,用户不需要或许指针的引用,此时利用const scoped_refptr&来返回值。(避免了用户在不需要的情况下,占用智能指针的引用计数的数量)

大部分Chromium代码遵循以上规则,如果没有的话,Google建议你清理掉它,改成正确的使用方式。

Forward declarations vs. #includes

  1. 与Google代码风格不同的是,Chromium代码风格趋向于提前声明,然后利用include来包含进来。这可以减少编译时间,并且当头文件改变的时候只需要少量的文件需要进行重新编译。
  2. 对于通过返回或者传递值、引用或者指针,或者通过指针进行存储的成员,或者STL的容器,你都可以使用提前声明大多数类型。然而,如果一个类型直接存储值是有意义的,请不要转化为指针,以便可以提前声明这个类型。

文件头

Chromium的所有文件都有一个相同的证书,例如:

// Copyright $YEAR The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

关于这个头文件的注意事项:

  1. There is no © after Copyright.
  2. $YEAR 在文件创建的时候就写定时间,在之后不能进行改变
  3. 特别是Chromium OS的文件,请将Chromium替换为Chromium OS。
  4. 如果代码风格变了,不要强行把旧文件的风格改成新风格。出于同样的原因,创建新文件的时候不要盲目的copy现用的文件头,因为旧文件可能用的是旧风格。
  5. Chromium项目包含了一些开源项目的镜像,所以当对某些库提交代码的时候保留现有文件头。

使用标准的#include来包含文件(可以看谷歌对于命名相关的约定)。不要使用#pragma once,历史原因不能支持所有平台,及时在它支持的平台上,它也没有#include表现好。

CHECK(), DCHECK(), and NOTREACHED()

如果条件不足,那么CHECK()会立即崩溃。DCHECK()像CHECK()但是只有当DCHECK_IS_ON 为true的时候才生效()。NOTREACHED()等价于DCHECK(false)。下面是一些使用这些的规则:

1、使用DCHECK()和NOTREACHED()来打断点,可以记录前置、后置条件。DCHECK()意为:这个条件一定要一直为true,而不是:这个条件一般为true。但是可能又特殊的情况,例如:磁盘损坏或网络异常,是不应该导致DCHECK()失败的。

2、你不应该处理DCHECK()错误的情况,即使出现崩溃。尝试处理DCHECK()失败是DCHECK()可能造成失败的语句,这与编写DCHECK()的观点相矛盾。特别是,不要像下面这样写代码:

  DCHECK(foo);
  if (!foo)  // Eliminate this code.使用了DCHECK再有这条语句就无效了
    ...
    

  if (!bar) {  // Replace this whole conditional with "DCHECK(bar);".这样安全一点
    NOTREACHED();
    return;
  }

3、当浏览器出现crash,并且是出现了安全漏洞的时候,使用CHECK()。因为这会占用整个浏览器的资源,所以有时候是可以选择更好的方式,而不是选择CHECK()。例如:renderer向browser进程发送一个格式不正确的IPC消息,那么黑客可能可以控制renderer进程,这个时候我们可以杀掉renderer进程还是不是crash这个浏览器进程。

4、在release版本中,可以暂时性的使用CHECK()来代替 DCHECK(),强行去造成crash来判断哪一个断言出现问题。最后不要留下这些代码当你把问题解决后。

5、在test的文件里不要使用这些宏,因为它会破坏二进制文件。使用ASSERT_xx() and EXPECT_xx()家族的宏,他们会友好的上报错误,并且继续运行其他的宏。

Miscellany(其他)

  1. 使用UTF-8文件编码格式,使用LF的文件结尾符
  2. 测试函数的单元测试和性能测试应该放在同样的目录下
  3. The C++ Dos and Don’ts page has more helpful information.
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值