Qt

Qt高级编码约定

2019-12-16  本文已影响0人  Qt君

本文翻译自: https://wiki.qt.io/Coding_Conventions
原作者: Qt
原文发布时间:2015年1月14日

  这是我们在编写Qt代码时使用的高级编码约定的概述。有关Qt代码规范,请参见Qt代码风格一文。对于QML,请参阅QML代码规范一文。

C++特性

Qt源代码中的约定

头文件包含

#include <qstring.h> /* Qt类头文件 */
#include <new>       /* STL 头文件 */
#include <limits.h> /* 系统头文件 */
#include <private/whatever_p.h>

类型转换

编译器/平台的特定问题

QString s;
return condition ? s : "nothing"; 
// 运行时崩溃:QString与const char *
union AlignHelper {
    char c;
    int i;
};
/* 全局作用域 */
static const QString x; /* 错误: 需要运行默认构造函数来初始化x。 */
static const QString y = "Hello"; /* 错误: 必须运行接受const char *的构造函数。 */
QString z; /* 超级错误行为! */
static const int i = foo(); /* 错误: foo()调用未定义,可能根本不会被调用。 */

你应该这样做:

/* 全局对象 */
static const char x[] = "someText"; /* 正常工作: 没有构造函数必须运行,x赋值在编译期。*/
static int y = 7; /* 正常工作: y将在编译期设置。*/
static MyStruct s = {1, 2, 3}; /* 正常工作: 编译期静态初始化。*/
static QString *ptr = 0; /* 指向对象的指针是ok的, 不需要运行代码来初始化ptr。*/

使用Q_GLOBAL_STATIC代替创建全局对象:

Q_GLOBAL_STATIC(QString, s)

void foo()
{
    s()->append("moo");
}

注意:作用域中的静态对象没有问题,在第一次使用时,构造函数将会运行。自C++ 11开始,这样的代码是可重入的。

char c; /* c不可能是负的,如果它是无符号的。*/
if (c > 0) { … } /* 不恰当的: c字符一致时无符号字符, 导致条件一直成立。*/

代码美感

避免的操作

A库:

class Q_EXPORT X: public QList<QVariant> {};

B库:

class Q_EXPORT Y: public QList<QVariant> {};

导致后果,QList<QVariant>在两个库中导出会报符号冲突的问题。

for (Container::const_iterator it = c.begin(); it != c.end(); ++it) /* 错误的 */
for (Container::const_iterator it = c.cbegin(); it != c.cend(); ++it) /* 正确 */
static QObject *obj = 0;
if (!obj)
    obj = new QObject(QCoreApplication::instance());

需要注意的是:如果QCoreApplication应用程序被销毁,则obj将是悬空指针。对静态全局对象使用Q_GLOBAL_STATIC或对qAddPostRoutine进行清理。

二进制和代码兼容性

命名空间

阅读命名空间中的Qt[https://wiki.qt.io/Qt_In_Namespace],并记住除测试和Webkit之外的所有Qt都是"namespaced"代码。

操作符

成员与非成员之间的决定[https://stackoverflow.com/questions/4421706/what-are-the-basic-rules-and-idioms-for-operator-overloading/4421729#4421729]

一个对两个参数都一视同仁的二元操作符不应该是成员。因为,除了上述链接提到的原因外。当运算符是成员时,参数也不相等。

QLineF的示例,可惜的是它的operator ==作为成员:

QLineF lineF;
QLine lineN;

if (lineF == lineN) /* 正确:lineN隐式转换为QLineF。 
if (lineN == lineF) /* 错误:QLineF无法隐式转换为QLine,并且LHS是成员,因此不进行转换。*/

如果operator ==在类之外,则转换规则将同样适用于双方。总结:范围小的值不能在前operator==使用

公共头文件的约定

我们的公共头文件必须在某些用户的严格设置下仍然有效。所有已安装的头文件都必须遵循以下规则:

class B: public A
{
    using A::val;
    int val(int x);
};
#if Foo == 0  /* 错误的 */
#if defined(Foo) && (Foo == 0) /* 正确的 */
#if Foo - 0 == 0 /* 自认为这种方法很聪明,是吗?请还是老老实实改用上面的正确方法,以提高可读。*/

C++11使用约定

注意:本节尚未被统一接受。本节将作为进一步讨论的基准。

Lambdas

您可以使用具有以下限制的lambda:

void Foo::something()
{
    ...
    std::generate(begin, end, []() { return Foo::someStaticFunction(); });
    ...
}

你应该使用简单的传递函数指针代替:

void Foo::something()
{
    ...
    std::generate(begin, end, &Foo::someStaticFunction);
    ...
}

为什么会出现这一规定(不能在lambda中使用类中的静态函数)?
因为是GCC 4.7和更早版本存在一个错误,需要捕获此错误,但如果您这样做,则Clang 5.0和更高版本将产生警告:

void Foo::something()
{
    ...
    std::generate(begin, end, [this]() { return Foo::someStaticFunction(); });
    /* 警告:不使用lambda捕获'this'[-Wunused-lambda-capture] */
    ...
}

根据以下规则格式化lambda:

[]() { doSomething(); }

不要这样写:

[] { doSomething(); }
[]() -> bool {
    something();
    return isSomethingElse();
}

不要这样写:

[]() -> bool { something();
    somethingElse(); }
foo([]() {
    something();
});
if (anyOf(fooList,
          [](Foo foo) {
              return foo.isGreat();
          })) {
    return;
}

不要这样写:

if (anyOf(fooList, [](Foo foo) {
          return foo.isGreat();
       })) {
    return;
}
foo([]() { return true; });

if (foo([]() { return true; })) {
    ...
}

auto关键词

(可选)在下列情况中,可以使用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君

上一篇下一篇

猜你喜欢

热点阅读