根据Qt官网提供的“Qt Coding Style”总结了以下中文版本的编码规范。Qt官网提供了两类编码风格,一类是【低级编码风格】一类是【高级编码风格】,以下是二者结合的版本。翻译有出入还请见谅!
1、声明变量
- 在单独的行上声明每个变量。
- 避免使用简短或无意义的名称(例如“a”、“rbarr”、“nughdeget”)。
- 只有计数器和临时变量可以使用单字符变量名,因为变量的目的显而易见。
- 声明变量时等待,直到需要它为止。
// Wrong
int a, b;
char *c, *d;
// Correct
int height;
int width;
char *nameOfThis;
char *nameOfThat;
- 变量和函数以小写字母开头。变量名称中的每个连续单词都以大写字母开头。
- 避免缩写。
// Wrong
short Cntr;
char ITEM_DELIM = ' ';
// Correct
short counter;
char itemDelimiter = ' ';
- 类总是以大写字母开头。公有类以 "Q"(QRgb)开头,后面跟一个大写字母。公共函数通常以 "q"(qRgb)开头。
- 首字母缩略词采用驼峰式命名(例如 QXmlStreamReader,而不是 QXMLStreamReader)。
2、花括号
- 使用附加大括号:左大括号与语句开头位于同一行。如果右大括号后面跟着另一个关键字,它也会进入同一行。
// Wrong
if (codec)
{
}
else
{
}
// Correct
if (codec) {
} else {
}
-
例外:函数实现(但不是 lambda )和类声明始终在行首有左大括号。
static void foo(int g)
{
qDebug("foo: %i", g);
}
class Moo
{
};
- = 默认值的函数实现将 = 默认值放在单独一行,并缩进:
// Wrong
Class::~Class() = default;
// Correct
Class::~Class()
= default;
- 基本原理:避免在用户定义与 = default 之间切换时必须触及不相关的行。
- 例外:在类内定义中,除了 = default 之外,不可能有任何其他内容,请将所有内容放在同一行上:
class MoveOnly {
Q_DISABLE_COPY(MoveOnly);
public:
~~~
MoveOnly(MoveOnly &&) = default; // OK
MoveOnly &operator=(MoveOnly) = default; // OK
~~~
};
- 仅当条件语句主体包含多行时才使用花括号:
// Wrong
if (address.isEmpty()) {
return false;
}
for (int i = 0; i < 10; ++i) {
qDebug("%i", i);
}
// Correct
if (address.isEmpty())
return false;
for (int i = 0; i < 10; ++i)
qDebug("%i", i);
- 例外 1:如果父语句涵盖多行/换行,也请使用大括号:
// Correct
if (address.isEmpty() || !isValid()
|| !codec) {
return false;
}
- 例外 2:大括号对称:在 if-then-else 块中也使用大括号,其中 if 代码或 else 代码覆盖多行:
// Wrong
if (address.isEmpty())
qDebug("empty!");
else {
qDebug("%s", qPrintable(address));
it;
}
// Correct
if (address.isEmpty()) {
qDebug("empty!");
} else {
qDebug("%s", qPrintable(address));
it;
}
// Wrong
if (a)
…
else
if (b)
…
// Correct
if (a) {
…
} else {
if (b)
…
}
- 当条件语句主体为空时使用花括号。
// Wrong
while (a);
// Correct
while (a) {}
3、括号
- 使用括号对表达式进行分组:
// Wrong
if (a && b || c)
// Correct
if ((a && b) || c)
// Wrong
a + b & c
// Correct
(a + b) & c
4、模板
- Qt 传统上在template-initializer的开头<前使用空格。但自从在 qt5.git 中添加了 _clang-format 文件,错误地将clang-format 配置为去掉空格后,Qt 中的样式就变得五花八门。因此,请选择您正在处理的文件/模块中最常用的样式。在 QtBase 中,这就是 with 空格。
// traditional Qt
template <typename T>
void foo();
// new-fangled
template<typename T>
void foo();
5、Switch语句
- 大小写标签与开关位于同一栏中。
- 每个 case 的结尾都必须有一个 break(或 return)语句,或者使用 Q_FALLTHROUGH() 来表示故意不中断,除非两个 case 标签之间没有代码。
switch (myEnum) {
case Value1:
doSomething();
break;
case Value2:
case Value3:
doSomethingElse();
Q_FALLTHROUGH();
default:
defaultHandling();
break;
}
6、跳转语句
- 不要在跳转语句后添加“else”:
-
例外:如果代码本质上是对称的,则允许使用 “else” ,以使对称性更加明显。
// Wrong
if (thisOrThat)
return;
else
somethingElse();
// Correct
if (thisOrThat)
return;
somethingElse();
7、间距
7.1、缩进
- 4个空格用于缩进
- 空格,而不是制表符!
7.2、换行符
- 行数不超过 100 个字符;必要时换行。
- 注释/apidoc 行应控制在实际文本的 80 列以下。根据周围环境进行调整,尽量使文字流畅,避免出现 "参差不齐 "的段落。
- 逗号放在已换行的末尾,而操作符则从新行的开头开始。如果编辑器太窄,在行尾的操作符很容易被忽略。
// Wrong
if (longExpression +
otherLongExpression +
otherOtherLongExpression) {
}
// Correct
if (longExpression
+ otherLongExpression
+ otherOtherLongExpression) {
}
- 在适合的情况下,使用空行将语句组合在一起。
- 始终只使用一个空行。
- 不要将多个语句放在一行上。
- 通过扩展,为控制流语句的主体使用新行:
// Wrong
if (foo) bar();
// Correct
if (foo)
bar();
7.3、代码中的空格
- 在流量控制关键字之后和大括号之前,始终使用单个空格。
// Wrong
if(foo){
}
// Correct
if (foo) {
}
- 对于指针或引用,类型与 "*"或"&"之间一定要使用单空格,但 " * " 或 " & "与变量名之间不能有空格 。
char *x;
const QString &myString;
const char * const y = "hello";
- 用空格包围二进制运算符。
- 每个逗号后留一个空格。
- 强制转换后没有空格(并避免 C 风格的强制转换)。
// Wrong
char* blockOfMemory = (char* ) malloc(data.size());
// Correct
char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size()));
8、一般例外情况
- 当严格遵守规则会让你的代码看起来很糟糕时,请随意打破它。
- 如果任何给定模块存在争议,维护者对接受的样式拥有最终决定权(根据 Qt 治理模型)。
9、工具支持
9.1、格式
您可以使用
clang-format
和
git-clang-format
来重新格式化您的代码。
qt5.git
仓库中有一个
_clang-format
文件, 试图为 Qt
代码编码规则。将该文件复制到根目录,让
clang-format
抓取它。
注意:** clang-format
无法配置为正确重现
Qt
风格,因此除非模块从一开始就是使用
clang-format
开发的,否则最好禁用 git-clang-format
。
QtBase
尤其如此。
9.2、艺术风格
艺术风格](
http://astyle.sourceforge.net/
)
可以使用以下代码片段来重新格式化您的代码。
--style=kr
--indent=spaces=4
--align-pointer=name
--align-reference=name
--convert-tabs
--attach-namespaces
--max-code-length=100
--max-instatement-indent=120
--pad-header
--pad-oper
请注意,使用 "
无限制
"--max-in-statement-indent
只是因为如果后续行需要缩进限制,
astyle
不会聪明到将第一个 参数包起来。我们鼓励你手动将 "in-statement-indent "
缩进限制在
50
列左右:
int foo = some_really_long_function_name(and_another_one_to_drive_the_point_home(
first_argument, second_argument, third_arugment));
10、C++特性
- 不要使用例外情况。
- 不要使用 rtti(运行时类型信息,即 typeinfo 结构、 dynamic_cast 或 typeid 操作符,包括抛出异常情况)。
- 明智地使用模板,而不是因为可以才使用。
- 提示:使用 compile 自动测试查看测试区中的所有编译器是否支持 C++ 功能。
11、Qt源代码中的约定
- 所有源代码都是UTF-8。
- 每个 QObject 子类都必须有一个 Q_OBJECT 宏,即使它没有信号或插槽,否则 qobject_cast 就会失败。
- 在 connect 语句中规范化信号 + 插槽的参数(参见 QMetaObject::normalizedSignature ),以获得更快的信号/插槽查找速度。您可以使用 qtrepotools/util/normalize 对现有代码进行规范化。
11.1、include guards
- 在公共和私有库标题中始终使用常规的 include guards:
// in qclassname.h
#ifndef QCLASSNAME_H
#define QCLASSNAME_H
// public declarations
#endif // QCLASSNAME_H
// in qclassname_p.h
#ifndef QCLASSNAME_P_H
#define QCLASSNAME_P_H
// private declarations
#endif // QCLASSNAME_P_H
在属于工具、示例和演示或测试的头文件中,如果 Qt
用户代码从未包含这些头文件,则可以使用
#pragma once 代替。理由:"#pragma once "
的定义不够完善,也不是标准的一部分。我们不能对
Qt
的安装方式、作为大型
SDK的一部分的使用方式等做出任何假设。为避免用户安装时出现问题,我们采用了保守的方法。为避免冲突,请确保新的类名和文件名在所有 Qt
子模块中都是唯一的。
11.2、包括标题
- 在公共头文件中,始终使用这种形式包含 Qt 头文件: #include <QtCore/qwhatever.h> 。对于 Mac OSX框架来说,库前缀是必要的,而且对于非 qmake 项目来说也非常方便。
- 在源文件中,先包含专用标题,再包含通用标题。用空行分隔不同类别。
#include <qstring.h>
// #include Qt stuff
// ...
#include <new>
// #include STL stuff
// ...
#include <limits.h>
// #include system stuff
// ...
- 如果您需要包含 qplatformdefs.h ,请始终将其作为第一个first头文件包含。
- 如果您需要包含私有头文件,请务必小心。请使用下面的语法,无论 whatever_p.h 位于哪个模块或目录中。
#include <private/whatever_p.h>
11.2、转换
- 避免 C 风格的使用,优先选择 C++ 风格的使用(static_cas、const_cas、reinterpret_cast)。
- 理由:reinterpret_cast 和 C 风格的使用都很危险,但至少 reinterpret_cast 不会移除 const 修饰符。
- 不要使用 "dynamic_cast",对 QObjects 使用 "qobject_cast "或重构设计,例如引入 type() 方法(参见QListWidgetItem)。
- 使用构造函数铸造简单类型:int(myFloat),而不是,(int)myFloat。
- 理由在重构代码时,编译器会立即让你知道是否会有危险。
11.3、编译器\平台特定问题
- 使用问号运算符时要格外小心。如果返回的类型不相同,某些编译器会生成在运行时崩溃的代码(您甚至不会收到编译器警告)。
QString s;
return condition ? s : "nothing"; // crash at runtime - QString vs. const char *
- 对齐时要格外小心。
- 每当指针被转换后,目标对齐方式的要求增加时,产生的代码在某些体系结构上可能会在运行时崩溃。例如,如果将 "const char *"转换为 "const int *",在整数必须以二或四字节边界对齐的机器上就会崩溃。
- 使用联合体可以强制编译器正确对齐变量。在下面的示例中,你可以确保 AlignHelper 的所有实例都以整数边界对齐。
union AlignHelper {
char c;
int i;
};
任何具有构造函数或需要运行代码才能初始化的东西都不能在库代码中作为全局对象使用,因为构造函数/
代码何时运行(首次使用时、库加载时、main()
之前或根本不运行)是未定义的。即使在共享库中定义了初始化器的执行时间,但在插件中移动该代码或在静态编译库时也会遇到麻烦。
C// global scope
static const QString x; // Wrong - default constructor needs to be run to initialize x
static const QString y = "Hello"; // Wrong - constructor that takes a const char * has to
be run
QString z; // super wrong
static const int i = foo(); // wrong - call time of foo() undefined, might not be called at
all
- 你可以做的事情:
// global scope
static const char x[] = "someText"; // Works - no constructor must be run, x set at compile
time
static int y = 7; // Works - y will be set at compile time
static MyStruct s = {1, 2, 3}; // Works - will be initialized statically, no code being run
static QString *ptr = 0; // Pointers to objects are ok - no code needed to be run to
initialize ptr
- 使用 Q_GLOBAL_STATIC 来创建静态全局对象:
Q_GLOBAL_STATIC(QString, s)
void foo()
{
s()->append("moo");
}
- Note: Static objects in a scope are no problem, the constructor will be run the first time the scope is entered. Such code is reentrant since C++11. 注意:作用域中的静态对象没有问题,构造函数将在第一次进入作用域时运行。从 C++11 开始,此类代码是可重入的。
- char 是否有符号取决于架构。如果您明确想要一个有符号/无符号字符,请使用 signed char 或 unsigned char 。在默认字符为无符号的平台上,以下代码中的条件始终为真。
char c; // c can't be negative if it is unsigned
/********/
/*******/
if (c > 0) { … } // WRONG - condition is always true on platforms where the default is
unsigned
- 避免使用 64 位枚举值。
- aapcs 嵌入的 ABI 将所有枚举值硬编码为 32 位整数。
- Microsoft 编译器不支持 64 位枚举值。 (已通过 Microsoft ® C/C++ 优化编译器版本15.00.30729.01 for x64 确认)
11.4、需要避免的事情
- 不要继承模板/工具类。
- 析构函数不是虚拟的,导致潜在的内存泄漏。
11.5、命名空间
请阅读
Qt In Namespace
,并牢记除
Tests
和
Webkit
之外的所有
Qt
都是
"
命名空间
"
代码。
12、C++ 11 使用约定
12.1、Lambda表达式
- 您可以使用具有以下限制的 lambda:
- 如果您使用 lambda 所在的类中的静态函数,请重构代码,以便不使用 lambda。例如:
void Foo::something()
{
...
std::generate(begin, end, []() { return Foo::someStaticFunction(); });
...
}
- 您也许可以简单地传递函数指针:
void Foo::something()
{
...
std::generate(begin, end, &Foo::someStaticFunction);
...
}
- 原因是 GCC 4.7 及更早版本有一个需要捕获 this 的错误,但如果您这样做,Clang 5.0 及更高版本将产生警告:
Cvoid Foo::something()
{
...
std::generate(begin, end, [this]() { return Foo::someStaticFunction(); });
// warning: lambda capture 'this' is not used [-Wunused-lambda-capture]
...
}
- 根据以下规则格式化 lambda:
- 即使函数不带参数,也始终为参数列表编写括号。
[]() { doSomething(); }
不是
[] { doSomething(); }
- 将捕获列表、参数列表、返回类型和左大括号放在第一行,将正文缩进到以下行,将右大括号放在新行。
[]() -> bool {
something();
return isSomethingElse();
}
不是
[]() -> bool { something();
somethingElse(); }
- 将封闭函数调用的右括号和分号与 lambda 的右大括号放在同一行。
foo([]() {
something();
});
- 如果您在“if”语句中使用 lambda,请在新行上开始 lambda,以避免 lambda 的左大括号与“if”语句的左大括号之间发生混淆。
if (anyOf(fooList,
[](Foo foo) {
return foo.isGreat();
})) {
return;
}
不是
if (anyOf(fooList, [](Foo foo) {
return foo.isGreat();
})) {
return;
}
- (可选)将 lambda 完全放置在一条线上(如果合适)。
foo([]() { return true; });
if (foo([]() { return true; })) {
...
}
12.2、自动关键字
- 您可以在以下情况下使用 auto 关键字。如果有疑问,例如使用 auto 可能会降低代码的可读性,请不要使用auto。请记住,代码的读取次数远多于编写次数。
- 当它避免在同一语句中重复类型时。
auto something = new MyCustomType;
auto keyEvent = static_cast<QKeyEvent *>(event);
auto myList = QStringList() << QLatin1String("FooThing") << QLatin1String("BarThing");
- 分配迭代器类型时。
auto it = myList.const_iterator();
------------------------------------------------------------分割线------------------------------------------------------------
以上是根据Qt官网翻译而来(原网址:Qt Coding Style - Qt Wiki)。如果需要PDF版请私信或者评论邮箱。