代码风格指南
缩进
使用空格,而不是制表符。制表符只应出现在因语义需要而必须使用的文件中,例如 Makefiles。
正确
int main()
{
return 0;
}
错误
int main()
{
return 0;
}
正确
// Document.h
namespace WebCore {
class Document {
Document();
...
};
namespace NestedNamespace {
class OtherDocument {
OtherDocument();
...
};
}
} // namespace WebCore
// Document.cpp
namespace WebCore {
Document::Document()
{
...
}
namespace NestedNamespace {
OtherDocument::OtherDocument()
{
...
}
} // namespace NestedNamespace
} // namespace WebCore
正确
// PrivateClickMeasurementDatabase.h
namespace WebKit::PCM {
class Database {
...
};
} // namespace WebKit::PCM
错误
// Document.h
namespace WebCore {
class Document {
Document();
...
};
namespace NestedNamespace {
...
}
} // namespace WebCore
// Document.cpp
namespace WebCore {
Document::Document()
{
...
}
} // namespace WebCore
case 标签应与对应的 switch 语句对齐。case 语句应缩进。
正确
switch (condition) {
case fooCondition:
case barCondition:
i++;
break;
default:
i--;
}
错误
switch (condition) {
case fooCondition:
case barCondition:
i++;
break;
default:
i--;
}
同一嵌套级别上跨多行的布尔表达式,其运算符应位于行的左侧而非右侧。
正确
return attribute.name() == srcAttr
|| attribute.name() == lowsrcAttr
|| (attribute.name() == usemapAttr && attribute.value().string()[0] != '#');
错误
return attribute.name() == srcAttr ||
attribute.name() == lowsrcAttr ||
(attribute.name() == usemapAttr && attr->value().string()[0] != '#');
空格
正确
i++;
错误
i ++;
正确
y = m * x + b;
f(a, b);
c = a | b;
return condition ? 1 : 0;
错误
y=m*x+b;
f(a,b);
c = a|b;
return condition ? 1:0;
正确
Vector<PluginModuleInfo> plugins;
for (auto& plugin : plugins)
registerPlugin(plugin);
错误
Vector<PluginModuleInfo> plugins;
for (auto& plugin: plugins)
registerPlugin(plugin);
正确
for (int i = 0; i < 10; ++i)
doSomething();
f(a, b);
错误
for (int i = 0 ; i < 10 ; ++i)
doSomething();
f(a , b) ;
正确
if (condition)
doIt();
错误
if(condition)
doIt();
正确
f();
void g() { ... }
h<int>();
错误
f ();
void g () { ... }
h <int> ();
函数声明或调用中的括号及其参数之间,或尖括号及其参数之间不应放置空格。
正确
f(a, b);
void g(int a) { ... }
h<int>();
错误
f( a, b );
void g( int a ) { ... }
h< int >();
lambda 函数的方括号、尖括号和括号之间不应放置空格,但大括号前应放置一个空格。
正确
[](int x) { return x; }
[this] { return m_member; }
[=]<typename T> { return T(); }
[&]<typename X>(X parameter) { return parameter; }
错误
[] (int x) { return x; }
[this]{ return m_member; }
[=] <typename T> { return T(); }
[&]<typename X> (X parameter) { return parameter; }
正确
template<typename T> T foo();
template<typename U> struct Bar { };
错误
template <typename T> T foo();
template <typename U> struct Bar { };
初始化对象时,在左大括号前以及大括号及其内容之间放置一个空格。
正确
Foo foo { bar };
错误
Foo foo{ bar };
Foo foo {bar};
在 Objective-C 中,块的开头与其参数之间,或块的开头与其开大括号之间不应放置空格。应在参数列表和块的开大括号之间放置一个空格。
正确
block = ^{
...
};
block = ^(int, int) {
...
};
错误
block = ^ {
...
};
block = ^ (int, int){
...
};
在 Objective-C 中,类型名称和协议名称之间不应放置空格。
正确
id<MTLDevice> device = ...;
错误
id <MTLDevice> device = ...;
换行
正确
x++;
y++;
if (condition)
doIt();
错误
x++; y++;
if (condition) doIt();
正确
rightSpacing = totalSpacing / 2;
leftSpacing = rightSpacing;
错误
leftSpacing = rightSpacing = totalSpacing / 2;
如果前面有闭合大括号,else
语句应与该闭合大括号在同一行;否则,它应与 if
语句对齐。
正确
if (condition) {
...
} else {
...
}
if (condition)
doSomething();
else
doSomethingElse();
if (condition)
doSomething();
else {
...
}
错误
if (condition) {
...
}
else {
...
}
if (condition) doSomething(); else doSomethingElse();
if (condition) doSomething(); else {
...
}
当之前的 if
语句以 return
语句结束时,else if
语句应写成一个 if
语句。
正确
if (condition) {
...
return someValue;
}
if (condition) {
...
}
错误
if (condition) {
...
return someValue;
} else if (condition) {
...
}
大括号
正确
int main()
{
...
}
错误
int main() {
...
}
正确
class MyClass {
...
};
namespace WebCore {
...
}
for (int i = 0; i < 10; ++i) {
...
}
错误
class MyClass
{
...
};
单行控制语句不应使用大括号,除非包含注释或单个语句跨越多行。
正确
if (condition)
doIt();
if (condition) {
// Some comment
doIt();
}
if (condition) {
myFunction(reallyLongParam1, reallyLongParam2, ...
reallyLongParam5);
}
错误
if (condition) {
doIt();
}
if (condition)
// Some comment
doIt();
if (condition)
myFunction(reallyLongParam1, reallyLongParam2, ...
reallyLongParam5);
正确
for ( ; current; current = current->next) { }
错误
for ( ; current; current = current->next);
正确
void f() { }
struct Unit { };
union Unit { };
class Unit { };
enum Unit { };
int x { };
auto a = [] { };
while (true) { }
错误
void f() {}
struct Unit {};
union Unit {};
class Unit {};
enum Unit {};
int x {};
auto a = [] {};
while (true) {}
空值、假值和零值
在 C++ 中,空指针值应写为 nullptr
。在 C 中,应写为 NULL
。在 Objective-C 和 Objective-C++ 中,分别遵循 C 或 C++ 的指导原则,但使用 nil
表示空 Objective-C 对象。
C++ 和 C 的 bool
值应写为 true
和 false
。Objective-C 的 BOOL
值应写为 YES
和 NO
。
对真/假、空/非空、零/非零的测试都应在不使用相等比较的情况下完成。
正确
if (condition)
doIt();
if (!ptr)
return;
if (!count)
return;
错误
if (condition == true)
doIt();
if (ptr == NULL)
return;
if (count == 0)
return;
在 Objective-C 中,实例变量会自动初始化为零。在 init 方法中不要添加显式初始化为 nil 或 NO。
浮点字面量
除非为了强制进行浮点运算,否则不应在浮点字面量后添加 .0
、.f
和 .0f
。
正确
const double duration = 60;
void setDiameter(float diameter)
{
radius = diameter / 2;
}
setDiameter(10);
const int framesPerSecond = 12;
double frameDuration = 1.0 / framesPerSecond;
错误
const double duration = 60.0;
void setDiameter(float diameter)
{
radius = diameter / 2.f;
}
setDiameter(10.f);
const int framesPerSecond = 12;
double frameDuration = 1 / framesPerSecond; // integer division
命名
使用驼峰命名法。在类、结构体、协议或命名空间名称中,首字母(包括首字母缩写中的所有字母)应大写。在变量或函数名称中,首字母(包括首字母缩写中的所有字母)应小写。
正确
struct Data;
size_t bufferSize;
class HTMLDocument;
String mimeType();
错误
struct data;
size_t buffer_size;
class HtmlDocument;
String MIMEType();
使用完整的单词,除非在极少数情况下,缩写更具规范性且更易于理解。
正确
size_t characterSize;
size_t length;
short tabIndex; // more canonical
错误
size_t charSize;
size_t len;
short tabulationIndex; // bizarre
C++ 类中的数据成员应为私有。静态数据成员应以“s_”为前缀。其他数据成员应以“m_”为前缀。
正确
class String {
public:
...
private:
short m_length;
};
错误
class String {
public:
...
short length;
};
正确
@class String
...
short _length;
@end
错误
@class String
...
short length;
@end
正确
bool isValid;
bool didSendData;
错误
bool valid;
bool sentData;
setter 方法应以“set”开头。getter 方法应使用裸词(即不加前缀)。setter 和 getter 的名称应与被设置/获取的变量名称匹配。
正确
void setCount(size_t); // sets m_count
size_t count(); // returns m_count
错误
void setCount(size_t); // sets m_theCount
size_t getCount();
通过输出参数返回值(out arguments)的 getter 方法应以“get”开头。
正确
void getInlineBoxAndOffset(InlineBox*&, int& caretOffset) const;
错误
void inlineBoxAndOffset(InlineBox*&, int& caretOffset) const;
正确
bool convertToASCII(short*, size_t);
错误
bool toASCII(short*, size_t);
成员变量的 getter 函数不应有任何后缀或前缀来指示该函数可以可选地创建或初始化成员变量。如果存在一个会自动创建对象的变体,则应将不自动创建对象的 getter 函数后缀为 IfExists
。
正确
StyleResolver* styleResolverIfExists();
StyleResolver& styleResolver();
错误
StyleResolver* styleResolver();
StyleResolver& ensureStyleResolver();
正确
Frame* frame();
错误
Frame* frameIfExists();
函数声明中应省略无意义的变量名。一个好的经验法则是,如果参数类型名包含参数名(不带尾随数字或复数),则不需要参数名。通常,布尔型、字符串型和数值型参数应有参数名。
正确
void setCount(size_t);
void doSomething(ScriptExecutionContext*);
错误
void setCount(size_t count);
void doSomething(ScriptExecutionContext* context);
如果调用者可能传递常量,则函数参数应优先使用枚举而不是布尔值,因为命名常量在调用点更容易阅读。此规则的一个例外是 setter 函数,其中函数名称已经清楚地表明了布尔值的含义。
正确
doSomething(something, AllowFooBar);
paintTextWithShadows(context, ..., textStrokeWidth > 0, isHorizontal());
setResizable(false);
错误
doSomething(something, false);
setResizable(NotResizable);
Objective-C 方法名称应遵循 Cocoa 命名指南——它们应读起来像一个短语,并且选择器的每个部分都应以小写字母开头并使用内部大写字母。
枚举成员应使用首字母大写的内部大写(InterCaps)命名法。
优先使用 const
而非 #define
。优先使用内联函数而非宏。
#defined
常量应使用全大写字母,单词之间用下划线分隔。
展开为函数调用或其他非常量计算的宏:这些宏应像函数一样命名,并且末尾应有括号,即使它们不接受任何参数(除了某些特殊宏,如 ASSERT)。请注意,在这种情况下通常优先使用内联函数而不是宏。
正确
#define WBStopButtonTitle()
NSLocalizedString(@"Stop", @"Stop button title")
错误
#define WB_STOP_BUTTON_TITLE
NSLocalizedString(@"Stop", @"Stop button title")
#define WBStopButtontitle
NSLocalizedString(@"Stop", @"Stop button title")
头文件防护应使用 #pragma once
而非 #define
和 #ifdef
。
正确
// HTMLDocument.h
#pragma once
错误
// HTMLDocument.h
#ifndef HTMLDocument_h
#define HTMLDocument_h
用于保护 this
免遭删除的 Ref 和 RefPtr 对象应命名为“protectedThis”。
正确
RefPtr<Node> protectedThis(this);
Ref<Element> protectedThis(*this);
RefPtr<Widget> protectedThis = this;
错误
RefPtr<Node> protector(this);
Ref<Node> protector = *this;
RefPtr<Widget> self(this);
Ref<Element> elementRef(*this);
用于保护 this
以外的变量免遭删除的 Ref 和 RefPtr 对象应命名为“protector”,或“protected”与变量名的大写形式结合。
正确
RefPtr<Element> protector(&element);
RefPtr<Element> protector = &element;
RefPtr<Node> protectedNode(node);
RefPtr<Widget> protectedMainWidget(m_mainWidget);
RefPtr<Loader> protectedFontLoader = m_fontLoader;
错误
RefPtr<Node> nodeRef(&rootNode);
Ref<Element> protect(*element);
RefPtr<Node> protectorNode(node);
RefPtr<Widget> protected = widget;
其他标点符号
C++ 类的构造函数应使用 C++ 初始化器语法初始化其所有成员。每个成员(和超类)应单独缩进一行,冒号或逗号在该行成员之前。
正确
MyClass::MyClass(Document* document)
: MySuperClass()
, m_myMember(0)
, m_document(document)
{
}
MyOtherClass::MyOtherClass()
: MySuperClass()
{
}
错误
MyClass::MyClass(Document* document) : MySuperClass()
{
m_myMember = 0;
m_document = document;
}
MyOtherClass::MyOtherClass() : MySuperClass() {}
在 Vector 迭代中,为了代码简洁易读,优先使用索引而非迭代器。
正确
for (auto& frameView : frameViews)
frameView->updateLayoutAndStyleIfNeededRecursive();
正确
unsigned frameViewsCount = frameViews.size();
for (unsigned i = 0; i < frameViewsCount; ++i)
frameViews[i]->updateLayoutAndStyleIfNeededRecursive();
错误
const Vector<RefPtr<FrameView> >::iterator end = frameViews.end();
for (Vector<RefPtr<FrameView> >::iterator it = frameViews.begin(); it != end; ++it)
(*it)->updateLayoutAndStyleIfNeededRecursive();
正确
[this] { return m_member; }
[this]() mutable { return doWork(WTFMove(m_object)); }
错误
[this]() { return m_member; }
[]() { return static_cast<unsigned>(-1); }
正确
int foo()
{
...
}
错误
auto foo() -> int
{
...
}
正确
auto Foo::bar() -> Baz
{
...
}
错误
Foo::Baz Foo::bar()
{
...
}
指针和引用
非 C++ 代码中的指针类型
指针类型应在类型和 *
之间放置一个空格(这样 *
就紧邻其后的标识符,如果有的话)。
C++ 代码中的指针和引用类型
指针类型和引用类型在类型名称与 *
或 &
之间不应有空格。
正确
Image* SVGStyledElement::doSomething(PaintInfo& paintInfo)
{
SVGStyledElement* element = static_cast<SVGStyledElement*>(node());
const KCDashArray& dashes = dashArray();
错误
Image *SVGStyledElement::doSomething(PaintInfo &paintInfo)
{
SVGStyledElement *element = static_cast<SVGStyledElement *>(node());
const KCDashArray &dashes = dashArray();
函数的输出参数应通过引用传递,除非在极少数情况下它是可选的,此时应通过指针传递。
正确
void MyClass::getSomeValue(OutArgumentType& outArgument) const
{
outArgument = m_value;
}
void MyClass::doSomething(OutArgumentType* outArgument) const
{
doSomething();
if (outArgument)
*outArgument = m_value;
}
错误
void MyClass::getSomeValue(OutArgumentType* outArgument) const
{
*outArgument = m_value;
}
#include 语句
所有实现文件必须首先 #include
config.h
。头文件不应包含 config.h
。
正确
// RenderLayer.h
#include "Node.h"
#include "RenderObject.h"
#include "RenderView.h"
错误
// RenderLayer.h
#include "config.h"
#include "RenderObject.h"
#include "RenderView.h"
#include "Node.h"
所有实现文件必须第二个 #include
主头文件,紧跟在 config.h
之后。例如,Node.cpp
应首先包含 Node.h
,然后才是其他文件。这确保了每个头文件的完整性得到测试。这也保证了每个头文件都可以在不先包含任何其他头文件的情况下编译。
其他 #include
语句应按排序顺序排列(区分大小写,如同命令行排序工具或 Xcode 排序选择命令所做的那样)。无需按逻辑顺序组织它们。
正确
// HTMLDivElement.cpp
#include "config.h"
#include "HTMLDivElement.h"
#include "Attribute.h"
#include "HTMLElement.h"
#include "QualifiedName.h"
错误
// HTMLDivElement.cpp
#include "HTMLElement.h"
#include "HTMLDivElement.h"
#include "QualifiedName.h"
#include "Attribute.h"
正确
// ConnectionQt.cpp
#include "ArgumentEncoder.h"
#include "ProcessLauncher.h"
#include "WebPageProxyMessageKinds.h"
#include "WorkItem.h"
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
错误
// ConnectionQt.cpp
#include "ArgumentEncoder.h"
#include "ProcessLauncher.h"
#include <QApplication>
#include <QLocalServer>
#include <QLocalSocket>
#include "WebPageProxyMessageKinds.h"
#include "WorkItem.h"
“using” 语句
在头文件中,不要在命名空间(或全局)作用域中使用“using”语句。
正确
// wtf/Vector.h
namespace WTF {
class VectorBuffer {
using std::min;
...
};
} // namespace WTF
错误
// wtf/Vector.h
namespace WTF {
using std::min;
class VectorBuffer {
...
};
} // namespace WTF
然而,在 WTF 子库的头文件中,允许在文件末尾使用“using”声明,将 WTF 命名空间中的一个或多个名称导入到全局作用域。
正确
// wtf/Vector.h
namespace WTF {
} // namespace WTF
using WTF::Vector;
错误
// wtf/Vector.h
namespace WTF {
} // namespace WTF
using namespace WTF;
错误
// runtime/JSObject.h
namespace WTF {
} // namespace WTF
using WTF::PlacementNewAdopt;
在 C++ 实现文件中,不要使用任何类型的“using”声明来导入标准模板库中的名称。而应在使用它们的地方直接限定名称。
正确
// HTMLBaseElement.cpp
namespace WebCore {
std::swap(a, b);
c = std::numeric_limits<int>::max()
} // namespace WebCore
错误
// HTMLBaseElement.cpp
using std::swap;
namespace WebCore {
swap(a, b);
} // namespace WebCore
错误
// HTMLBaseElement.cpp
using namespace std;
namespace WebCore {
swap(a, b);
} // namespace WebCore
在实现文件中,如果“using namespace”语句是针对其父命名空间在该文件中定义的嵌套命名空间,则将该语句放在该命名空间定义内部。
正确
// HTMLBaseElement.cpp
namespace WebCore {
using namespace HTMLNames;
} // namespace WebCore
错误
// HTMLBaseElement.cpp
using namespace WebCore::HTMLNames;
namespace WebCore {
} // namespace WebCore
在实现文件中,将所有“using namespace”语句放在命名空间定义内部。
正确
// HTMLSelectElement.cpp
namespace WebCore {
using namespace other;
} // namespace WebCore
错误
// HTMLSelectElement.cpp
using namespace other;
namespace WebCore {
} // namespace WebCore
Lambda 表达式
当函数体中需要参数的显式类型时,优先使用带有显式模板参数列表的 lambda 表达式。
正确
[]<typename T>(T arg) {
if constexpr (T::isGood)
go();
}
错误
[](auto arg) {
using T = std::decay_t<decltype(arg)>;
if constexpr (T::isGood)
go();
}
类型
使用“unsigned”修饰符时省略“int”。不要使用“signed”修饰符。直接使用“int”即可。
正确
unsigned a;
int b;
错误
unsigned int a; // Doesn't omit "int".
signed b; // Uses "signed" instead of "int".
signed int c; // Doesn't omit "signed".
类
当参数合理地被认为是类型转换且类型转换很快时,使用构造函数进行隐式转换。否则,使用 explicit 关键字或返回该类型的函数。这仅适用于单参数构造函数。
正确
class LargeInt {
public:
LargeInt(int);
...
class Vector {
public:
explicit Vector(int size); // Not a type conversion.
Vector create(Array); // Costly conversion.
...
错误
class Task {
public:
Task(ScriptExecutionContext&); // Not a type conversion.
explicit Task(); // No arguments.
explicit Task(ScriptExecutionContext&, Other); // More than one argument.
...
单例模式
使用名为“singleton()”的静态成员函数来访问单例实例。
正确
class MySingleton {
public:
static MySingleton& singleton();
...
错误
class MySingleton {
public:
static MySingleton& shared();
...
错误
class MySingleton {
...
};
MySingleton& mySingleton(); // free function.
注释
正确
f(a, b); // This explains why the function call was done. This is another sentence.
错误
int i; // This is a comment with several spaces before it, which is a non-conforming style.
double f; // This is another comment. There are two spaces before this sentence which is a non-conforming style.
注释应像句子一样,以大写字母开头,并以句号(标点符号)结尾。一个例外是行末注释,例如 if (x == y) // false for NaN
。
正确
drawJpg(); // FIXME: Make this code handle jpg in addition to the png support.
错误
drawJpg(); // FIXME(joe): Make this code handle jpg in addition to the png support.
drawJpg(); // TODO: Make this code handle jpg in addition to the png support.
如果为了清晰起见,在一段虚函数重写声明前添加包含源类名称的注释。
正确
class GPUProcessConnection : public RefCounted<GPUProcessConnection>, public IPC::Connection::Client {
public:
/// ...
// IPC::Connection::Client
void didClose(IPC::Connection&) override;
void didReceiveMessage(IPC::Connection&, IPC::Decoder&) final;
bool didReceiveSyncMessage(IPC::Connection&, IPC::Decoder&, UniqueRef<IPC::Encoder>&) final;
void didReceiveInvalidMessage(IPC::Connection&, IPC::MessageName) override;
// ...
};
如果为了清晰起见,在一组 IPC 消息函数声明前添加 // Messages
。
正确
class GPUProcessConnection : public RefCounted<GPUProcessConnection>, public IPC::Connection::Client {
public:
/// ...
// Messages
void didReceiveRemoteCommand(WebCore::PlatformMediaSession::RemoteControlCommandType, const WebCore::PlatformMediaSession::RemoteCommandArgument&);
void didInitialize(std::optional<GPUProcessConnectionInfo>&&);
// ...
};
重写虚方法
类中虚方法的基层声明必须使用 virtual
关键字声明。该类的所有子类在重写虚方法时必须指定 override
关键字,或者在重写虚方法并要求后续子类不能再重写它时指定 final
关键字。切勿用 virtual
、override
或 final
关键字中的多个来修饰一个方法。
正确
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
String description() override { ... }; // This is correct because it only contains the "override" keyword to indicate that the method is overridden.
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
String description() final { ... }; // This is correct because it only contains the "final" keyword to indicate that the method is overridden and that no subclasses of "Student" can override "description".
}
错误
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() override { ... }; // This is incorrect because it uses both the "virtual" and "override" keywords to indicate that the method is overridden. Instead, it should only use the "override" keyword.
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() final { ... }; // This is incorrect because it uses both the "virtual" and "final" keywords to indicate that the method is overridden and final. Instead, it should only use the "final" keyword.
}
class Person {
public:
virtual String description() { ... };
}
class Student : public Person {
public:
virtual String description() { ... }; // This is incorrect because it uses the "virtual" keyword to indicate that the method is overridden.
}