coding-style.html

代码规范很重要,这决定了编码风格的统一。如果你要向qt贡献代码,那么你需要看和他们一致。当然,你也可以学习一下qt的经验,为什么人家要采用这种风格,有什么好处!


编码规则旨在指导Qt Creator开发人员,帮助他们编写可理解和可维护的代码,并最大程度地减少混乱和意外。

提交代码

略。

二进制兼容性和源代码兼容性

以下列表描述了发行版的编号方式,并定义发行版之间的二进制兼容性和源代码兼容性:

  • Qt Creator 3.0.0是主要版本,Qt Creator 3.1.0是次要版本,Qt Creator 3.1.3是补丁版本。

  • 后向二进制兼容性意味着,代码链接到库的旧版本仍然有效。

  • 前向二进制兼容性意味着,可与旧库一起使用的代码,可链接到库的新版本。

  • 源代码兼容性意味着代码无需修改即可编译。

当前,我们不保证主要版本和次要版本之间的二进制或源代码兼容性。

但是,在同一次要版本中,我们尝试为补丁版本保留后向二进制兼容性和后向源代码兼容性

  • 软API冻结:从次要版本的Beta发布之后不久,我们就开始在该次要版本中保持后向源代码兼容性,包括其补丁版本。这意味着从那时起,使用Qt Creator API的代码将针对此次要版本的所有后续版本的API进行编译,包括其补丁版本。该规则偶尔可能会有例外,应该适当地加以传达。

  • 硬API冻结:从次要版本的候选版本开始,我们在该次要版本保持后向源代码兼容性,包括其补丁版本。我们还将开始保持后向二进制兼容性,但不会在插件的compatVersion设置中反映出来。因此,针对候选发行版编写的Qt Creator插件,实际上不会在最终版本或更高版本中加载,即使这些库的二进制兼容性在理论上是允许的。请参阅下面有关插件规格的部分。

  • 硬ABI冻结:从次要版本的最终发行开始,我们保持该版本及其所有补丁版本的后向源代码和二进制兼容性。

为了保持向后兼容性:

  • 请勿添加或删除任何公有API(例如,全局函数,公有/受保护/私有成员函数)。

  • 不要重新实现功能(甚至是内联函数,受保护的或私有的函数)。

  • 检查二进制兼容性替代方法,以获取保留二进制兼容性的方法。

有关二进制兼容性的更多信息,请参见C++二进制兼容性问题

从插件规范的角度来看,这意味着

  • 补丁版本中的Qt Creator插件将把次要版本作为compatVersion值。 例如,3.1.2版本的插件具有compatVersion ="3.1.0"。

  • 次要版本的预发行(包括候选发行)中,把它们自己作为compatVersion值,这意味着针对最终发行版编译的插件将不会在预发行版中加载。

  • Qt Creator插件开发人员可以决定他们的插件是否需要某个补丁版本(或者更高版本),或者通过在声明对其他插件的依赖性时设置相应的version值,他们可以使用此次要版本的所有补丁版本。Qt项目提供的Qt Creator插件的默认设置是要求最新的补丁版本。

例如,Qt Creator 3.1 beta(内部版本号3.0.82)中的Find插件将具有

  <plugin name="Find" version="3.0.82" compatVersion="3.0.82">
<dependencyList>
<dependency name="Core" version="3.0.82"/>
....

Qt Creator 3.1.0 final中的Find插件将具有

  <plugin name="Find" version="3.1.0" compatVersion="3.1.0">
<dependencyList>
<dependency name="Core" version="3.1.0"/>
....

Qt Creator 3.1.1补丁版本中的Find插件将具有版本3.1.1,将向后二进制兼容Find插件版本3.1.0(compatVersion ="3.1.0"),并且将依赖向后二进制兼容的Core插件版本3.1.1:

  <plugin name="Find" version="3.1.1" compatVersion="3.1.0">
<dependencyList>
<dependency name="Core" version="3.1.1"/>
....

代码构造

遵循代码构造准则,以使代码更快,更清晰。 此外,该准则还允许您利用C++中的强类型检查。

  • 在可能的情况下,建议前缀递增,而不是后缀递增。前缀递增可能更快。只要考虑一下前/后递增的明显实现即可。 此规则也适用于递减:

      ++T;
    --U; -NOT- T++;
    U--;
  • 尝试尽量减少对同一代码的重复计算。 特别是循环的时候:

      Container::iterator end = large.end();
    for (Container::iterator it = large.begin(); it != end; ++it) {
    ...;
    } -NOT- for (Container::iterator it = large.begin();
    it != large.end(); ++it) {
    ...;
    }
  • 您可以在非时间紧缺的代码中,对Qt容器使用Qt foreach循环。 这是降低多行冗余,并给循环变量起适当名称的一种好方法:

    foreach (QWidget *widget, container)
    doSomething(widget); -NOT- Container::iterator end = container.end();
    for (Container::iterator it = container.begin(); it != end; ++it)
    doSomething(*it);

    如果可能,将循环变量设为const。 这可以防止不必要的共享数据分离:

      foreach (const QString &name, someListOfNames)
    doSomething(name); - NOT - foreach (QString name, someListOfNames)
    doSomething(name);

格式化

利用标识符

在标识符中使用驼峰大小写

充分利用标识符中的第一个字符,如下所示:

  • 类名以大写字母开头。
  • 函数名称以小写字母开头。
  • 变量名以小写字母开头。
  • 枚举名称和值以大写字母开头。 无作用域限制的Enum值包含枚举类型的部分名称。

空格

  • 使用四个空格来进行缩进,不要使用制表符Tab。
  • 使用空白行将合适的代码组合在一起。
  • 始终仅使用一个空白行。

指针和引用

对于指针或引用,请始终在星号(*)或与号(&)之前使用单个空格,但不要在其后使用。 尽可能避免使用C语言类型转换:

  char *blockOfMemory = (char *)malloc(data.size());
char *blockOfMemory = reinterpret_cast<char *>(malloc(data.size())); -NOT- char* blockOfMemory = (char* ) malloc(data.size());

当然,在这个特殊的例子中,可能使用new更合适。

Operator名称和括号

operator名称和括号之间请勿使用空格。 等式标记(==)是operator名称的一部分,因此,空格使声明看起来像一个表达式:

  operator==(type)

  -NOT-

  operator == (type)

函数名称和括号

不要在函数名称和括号之间使用空格:

  void mangle()

  -NOT-

  void mangle ()

关键词

请始终在关键字和大括号之间使用一个空格:

  if (foo) {
} -NOT- if(foo){
}

注释

通常,在“ //”之后放置一个空格。 要对齐多行注释中的文本,可以插入多个空格。

大括号

作为基本规则,将左大括号与语句开头放在同一行:

  if (codec) {
} -NOT- if (codec)
{
}

例外:函数实现和类声明的左括号始终在行首处:

  static void foo(int g)
{
qDebug("foo: %i", g);
} class Moo
{
};

当条件语句的主体包含多行时,以及单行语句有些复杂时,请使用大括号。 否则,请省略它们:

  if (address.isEmpty())
return false; for (int i = 0; i < 10; ++i)
qDebug("%i", i); -NOT- if (address.isEmpty()) {
return false;
} for (int i = 0; i < 10; ++i) {
qDebug("%i", i);
}

例外1:如果括号中的语句包含多行或进行了换行,也请使用大括号:

  if (address.isEmpty()
|| !isValid()
|| !codec) {
return false;
}

注意:这可以重写为:

  if (address.isEmpty())
return false; if (!isValid())
return false; if (!codec)
return false;

例外2:在if-then-else块中,如果if-code或else-code覆盖多行,也请使用大括号:

  if (address.isEmpty()) {
--it;
} else {
qDebug("%s", qPrintable(address));
++it;
} -NOT- if (address.isEmpty())
--it;
else {
qDebug("%s", qPrintable(address));
++it;
}
  if (a) {
if (b)
...
else
...
} -NOT- if (a)
if (b)
...
else
...

当条件语句的主体为空时,请使用大括号:

  while (a) {}

  -NOT-

  while (a);

圆括号

使用括号将表达式分组:

  if ((a && b) || c)

  -NOT-

  if (a && b || c)
  (a + b) & c

  -NOT-

  a + b & c

换行符

  • 行少于100个字符。
  • 如有必要,请插入换行符。
  • 逗号放在行的结尾。
  • 操作符放在新行的开头。
if (longExpression
|| otherLongExpression
|| otherOtherLongExpression) {
} -NOT- if (longExpression ||
otherLongExpression ||
otherOtherLongExpression) {
}

声明

  • 对类的访问部分使用以下顺序:公有,受保护,私有。 公有部分对类的每个用户都很有趣。 私有部分仅对该类的实现者有用。
  • 在类的声明文件中,避免声明全局对象。 如果所有对象都使用相同的变量,请使用静态成员。
  • 使用class而不是struct。一些编译器会在符号名称中混入差异,并在struct声明后跟class声明时发出警告。 为了避免持续更改来消除警告,我们将class声明作为首选方式。

声明变量

  • 避免使用类类型的全局变量,来排除初始化顺序的问题。 如果无法避免,请考虑使用Q_GLOBAL_STATIC。

  • 声明全局字符串文本

    const char aString[] = "Hello";
  • 尽可能避免使用短名称(例如a,rbarr,nughdeget)。仅将单字符变量名称用于计数器和临时变量,其用途是显而易见。

  • 每个变量声明在单独的行中:

      QString a = "Joe";
    QString b = "Foo"; -NOT- QString a = "Joe", b = "Foo";

    注意:QString a = "Joe"首先创建了由字符串文字构造的临时副本,然后调用拷贝构造函数。 因此,它可能比通过QString a("Joe")直接构造更昂贵。但是,编译器可以删除副本(即使这样做有副作用),现代编译器通常会这样做。 考虑到这些相等的代价,Qt Creator代码还是倾向于使用'='习惯用法,因为它符合传统的C样式初始化,它不会被误认为是函数声明,并且它减少了更多初始化中的嵌套括号的级别。

  • 避免缩写:

      int height;
    int width;
    char *nameOfThis;
    char *nameOfThat; -NOT- int a, b;
    char *c, *d;
  • 当变量被需要时,才进行声明。声明时完成初始化,这一点尤其重要。

命名空间

  • 将左大括号与namespace关键字放在同一行。

  • 不要缩进内部的声明或定义。

  • 可选,但如果名称空间跨越多行,则建议使用:在右大括号后添加注释,重复该名称空间。

      namespace MyPlugin {
    
      void someFunction() { ... }
    
          }  // namespace MyPlugin
  • 作为例外,如果名称空间中只有一个类声明,则可以都放在一行上:

    namespace MyPlugin { class MyClass; }
  • 不要在头文件中使用using指令。

  • 定义类和函数时不要依赖using指令,而应在适当的命名声明区域中对其进行定义。

  • 访问全局函数时,请勿依赖using指令。

  • 在其他情况下,建议您使用using指令,因为它们可帮助您避免混乱的代码。 最好将所有的using指令放在文件顶部附近,#include之后。

      [in foo.cpp]
    ...
    #include "foos.h"
    ...
    #include <utils/filename.h>
    ...
    using namespace Utils; namespace Foo {
    namespace Internal { void SomeThing::bar()
    {
    FileName f; // or Utils::FileName f
    ...
    }
    ...
    } // namespace Internal // or only // Internal
    } // namespace Foo // or only // Foo -NOT- [in foo.h]
    ...
    using namespace Utils; // Wrong: no using-directives in headers class SomeThing
    {
    ...
    }; -NOT- [in foo.cpp]
    ...
    using namespace Utils; #include "bar.h" // Wrong: #include after using-directive -NOT- [in foo.cpp]
    ...
    using namepace Foo; void SomeThing::bar() // Wrong if Something is in namespace Foo
    {
    ...
    }

模式与实践

命名空间

阅读Qt In Namespace,并记住所有Qt Creator都是名称空间感知的代码。

Qt Creator中的命名空间策略如下:

  • 供其他库或插件使用的,被导出的库或插件中的类/符号,位于库/插件指定的命名空间中,例如MyPlugin。
  • 未被导出的库或插件的类/符号,位于库/插件的附加的内部名称空间中,例如MyPlugin::Internal。

传递文件名

Qt Creator API需要使用可移植格式的文件名,即,在Windows上也要使用斜杠(/)而不是反斜杠(\)。传递用户输入的文件名给API,请先使用QDir::fromNativeSeparators对其进行转换。要向用户显示文件名,请使用QDir::toNativeSeparators将其转换回本地格式。对于这些任务,请考虑使用Utils::FileName::fromUserInput(QString)和Utils::FileName::toUserOutput()。

比较文件名时请使用Utils::FileName,因为它是大小写敏感的。还要确保文件夹使用的路径,是没有使用相对路径的(QDir::cleanPath())。

插件扩展点

插件扩展点是一个插件提供的接口,供其他人实现。然后,插件将检索接口的所有实现,并使用这些实现。也就是说,它们扩展了插件的功能。通常,接口的实现会在插件初始化期间放入全局对象池中,在插件初始化结束时,插件即可从对象池中检索到它们。

例如,Find插件为其他插件提供了FindFilter接口。在FindFilter界面中,可以添加其他搜索范围,并会显示在高级搜索对话框中。 Find插件从全局对象池中检索所有FindFilter实现,并将其显示在对话框中。该插件将实际搜索请求转发到正确的FindFilter实现,然后执行搜索。

使用全局对象池

通过ExtensionSystem::PluginManager::addObject(),您可以将对象添加到全局对象池,并通过ExtensionSystem::PluginManager::getObjects(),再次获取特定类型的对象。这应该主要用于插件扩展点的实现。

注意:请勿将单例放入对象池中,也不要从中检索它。请改用单例模式。

C++特征

  • 最好是#pragma once,而不是文件保护符。

  • 除非您知道自己要做什么,否则不要使用异常。

  • 除非您知道要做什么,否则不要使用RTTI(运行时类型信息;即typeinfo结构,dynamic_cast或typeid运算符,包括抛出异常)。

  • 除非您知道自己要做什么,否则不要使用虚拟继承。

  • 明智地使用模板,不要只因为你会模板。

    提示:使用编译期自动测试,可查看C++功能是否被测试场中的所有编译器支持。

  • 所有代码都是ASCII(仅仅7bit字符,如果不确定,请运行man ascii)

    • 理由:我们内部的语言环境太多,UTF-8和Latin1系统的组合不健康。通常,数值大于127的字符,在您最喜欢的编辑器中进行单机保存的不知情情况下,就可能已被破坏。

    • 对于字符串:使用\nnn(其中nnn是对你字符串所在的任何语言环境中的八进制表示)或\xnn(其中nn是十六进制)。例如:QString s = QString::fromUtf8("\213\005")

    • 对于文档中的变音符号或其他非ASCII字符,请使用qdoc \unicode命令或使用相关的宏。例如:\uuml表示ü。

  • 尽可能使用静态关键字代替匿名命名空间。局部化到编译单元的名称,使用static可具有内部链接性。对于在匿名命名空间中声明的名称,不幸的是C++标准要求外部链接性(ISO/IEC 14882, 7.1.1/6, 或在gcc邮件列表上查看有关此内容的各种讨论)。

空指针

为空指针常量使用平凡零(0)始终是正确的,并且键入最少。

  void *p = 0;

  -NOT-

  void *p = NULL;

  -NOT-

  void *p = '\0';

  -NOT-

  void *p = 42 - 7 * 6;

注意:作为例外,导入的第三方代码以及与本机API接口的代码(src/support/os_*)可以使用NULL。

C ++ 11和C ++ 14功能

代码应与Microsoft Visual Studio 2013,g++ 4.7和Clang 3.1一起编译。

Lambdas

使用lambda时,请注意以下几点:

  • 您不必显式指定返回类型。 如果您没有使用前面提到的编译器,请注意这是C++ 14功能,您可能需要在编译器中启用C++ 14支持。

      []() {
    Foo *foo = activeFoo();
    return foo ? foo->displayName() : QString();
    });

如果您在类函数实现中使用了lambda,并调用了所在类的静态函数,则必须明确捕获this。 否则,它将无法使用g++ 4.7及更早版本进行编译。

  void Foo::something()
{
...
[this]() { Foo::someStaticFunction(); }
...
} -NOT- void Foo::something()
{
...
[]() { Foo::someStaticFunction(); }
...
}

根据以下规则格式化lambda:

  • 把捕获列表,参数列表,返回类型和左括号放在第一行,在接下来的几行中缩进主体,在新的一行上关闭右括号。

      []() -> bool {
    something();
    return isSomethingElse();
    } -NOT- []() -> bool { something();
    somethingElse(); }
  • 将函数调用的右圆括号和分号与lambda的右大括号放在同一行。

  foo([]() {
something();
});
  • 如果在'if'语句中使用lambda,请在新行上启动lambda,以避免在lambda的左大括号和'if'语句的左大括号之间造成混淆。

      if (anyOf(fooList,
    [](Foo foo) {
    return foo.isGreat();
    }) {
    return;
    } -NOT- if (anyOf(fooList, [](Foo foo) {
    return foo.isGreat();
    }) {
    return;
    }
  • 可选,如果合适,将lambda完整地放在同一行。

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

auto关键字

可选,在以下情况下,可以使用auto关键字。 如有疑问,例如,如果使用auto会使代码的可读性降低,请不要使用auto。 请记住,代码的读取次数比编写的次数要多。

  • 避免在同一条语句中重复某个类型。

      auto something = new MyCustomType;
    auto keyEvent = static_cast<QKeyEvent *>(event);
    auto myList = QStringList({ "FooThing", "BarThing" });
  • 分配迭代器类型时

      auto it = myList.const_iterator();

域化枚举

不需要将非域化枚举隐式转换为int,或附加域有用时,使用域化枚举。

委托构造函数

如果多个构造函数使用基本上相同的代码,请使用委托构造函数。

初始化列表

使用初始化列表来初始化容器,例如:

  const QVector<int> values = {1, 2, 3, 4, 5};

用大括号初始化

如果对大括号使用初始化,请遵循与圆括号相同的规则。 例如:

  class Values // the following code is quite useful for test fixtures
{
float floatValue = 4; // prefer that for simple types
QVector<int> values = {1, 2, 3, 4, integerValue}; // prefer that syntax for initializer lists
SomeValues someValues{"One", 2, 3.4}; // not an initializer_list
SomeValues &someValuesReference = someValues;
ComplexType complexType{values, otherValues} // constructor call
} object.setEntry({"SectionA", value, doubleValue}); // calls a constructor
object.setEntry({}); // calls default constructor

但是请注意不要过度使用它,以免混淆您的代码。

非静态数据成员初始化

使用非静态数据成员初始化进行平凡(trivial)的初始化,但在公共导出类中除外。

默认函数和删除函数

考虑使用=default和=delete来控制特殊函数。

覆写

覆写虚函数时,建议使用override关键字。不要在已覆写函数上使用virtual。

确保一个类对所有被覆盖的函数始终使用override,以区分没有被覆盖的。

Nullptr

所有编译器都支持nullptr,但在使用方面尚未达成共识。 如有疑问,请询问模块的维护者是否更喜欢使用nullptr。

基于范围的for循环

您可以使用基于范围的for循环,但要注意虚假分离问题。 如果for循环仅读取容器,且容器是const的还是不共享的,不明显,请使用std::cref()来确保不会不必要地分离容器。

使用QObject

  • 请记住,将Q_OBJECT宏添加到依赖于元对象系统的QObject子类中。 与元对象系统相关的功能包括信号和槽的定义,qobject_cast <>的使用等。 另请参阅Casting。
  • 优先使用Qt5样式的connect()调用,而不是Qt4样式的。
  • 使用Qt4样式的connect()调用时,请在connect语句中标准化信号和槽的参数,以使更加快速安全地查找信号和槽。 您可以使用$QTDIR/util/normalize来标准化现有代码。 有关更多信息,请参见QMetaObject::normalizedSignature。

文件头

如果创建一个新文件,则文件顶部应包含头注释,保持与Qt Creator其他源文件中的一样。

包含头文件

  • 使用以下格式来包含Qt标头:#include 。 不要包括可能在Qt4和Qt5之间进行了更改的模块。

  • 排列顺序应从特定到通用,以确保头文件是齐全的。 例如:

    • #include "myclass.h"
    • #include "otherclassinplugin.h"
    • #include <otherplugin/someclass.h>
    • #include <QtClass>
    • #include <stdthing>
    • #include <system.h>
  • 将其他插件的头文件括在方括号(<>)而不是引号("")中,以便更轻松地发现源代码中的外部依赖项。

  • 在同样性质头文件的长块之间添加空行,并尝试按字母顺序在块内排列头文件。

Casting

  • 避免使用C强制转换,最好使用C++强制转换(static_cast,const_cast,reinterpret_cast)reinterpret_cast和C类型强制转换都是危险的,但是至少reinterpret_cast不会删除const修饰符。
  • 除非您知道要做什么,否则不要使用dynamic_cast,对QObject使用qobject_cast或重构设计,例如引入type()函数(请参阅QListWidgetItem)。

编译器和平台特定的问题

  • 使用问号运算符时要格外小心。 如果返回的类型不相同,则某些编译器会生成的代码会在运行时崩溃(您甚至不会收到编译器警告):

      QString s;
    // crash at runtime - QString vs. const char *
    return condition ? s : "nothing";
  • 对齐时要格外小心。

    每当指针转换时,目标所需对齐字节数增加,在某些体系结构上,生成的代码可能会在运行时崩溃。 例如,如果将const char *强制转换为const int *,它将在整数两字节对齐或四个字节对齐的机器上崩溃。

    使用union来强制编译器正确对齐变量。 在下面的示例中,可以确保AlignHelper的所有实例int边界对齐:

      union AlignHelper
    {
    char c;
    int i;
    };
  • 对于头文件中的静态声明,请坚持使用整数类型,整数类型数组及其结构。 例如,static float i[SIZE_CONSTANT]; 在大多数情况下,不会在每个插件中进行优化和拷贝,最好避免使用。

  • 任何具有构造函数或需要运行代码进行初始化的对象,都不能作为库代码的全局对象,因为该构造函数或代码何时运行未定义(首次使用时,库加载时,main()之前或根本不进行)。

    即使为共享库定义了初始化程序的执行时间,移动该代码到插件中或库是静态编译时,你会遇到麻烦:

      // global scope
    
      -NOT-
    
      // Default constructor needs to be run to initialize x:
    static const QString x; -NOT- // Constructor that takes a const char * has to be run:
    static const QString y = "Hello"; -NOT- QString z; -NOT- // Call time of foo() undefined, might not be called at all:
    static const int i = foo();

    你应该这样做:

      // global scope
    // No constructor must be run, x set at compile time:
    static const char x[] = "someText"; // y will be set at compile time:
    static int y = 7; // Will be initialized statically, no code being run.
    static MyStruct s = {1, 2, 3}; // Pointers to objects are OK, no code needed to be run to
    // initialize ptr:
    static QString *ptr = 0; // Use Q_GLOBAL_STATIC to create static global objects instead: Q_GLOBAL_STATIC(QString, s) void foo()
    {
    s()->append("moo");
    }

    注意:函数范围内的静态对象没有问题。构造函数将在第一次函数进入时运行。但是,该代码不是可重入的。

  • char是有符号的还是无符号的,取决于体系结构。 如果您明确需要有符号或无符号的char,请使用有符号char或uchar。 例如,以下代码将在PowerPC上中断:

      // Condition is always true on platforms where the
    // default is unsigned:
    if (c >= 0) {
    ...
    }
  • 避免使用64位枚举值。 嵌入式AAPCS(ARM体系结构的过程调用标准)的ABI将所有枚举值都硬编码为32位整数。

  • 不要混合使用const和非const迭代器。 这将在被破坏的编译器上悄无声息地崩溃。

  for (Container::const_iterator it = c.constBegin(); it != c.constEnd(); ++it)

  -NOT-

  for (Container::const_iterator it = c.begin(); it != c.end(); ++it)
  • 不要在导出的类中内联虚析构函数。 这会导致依赖插件中的vtable表重复,这也可能破坏RTTI。参见QTBUG-45582.

美学

  • 优先选择域化枚举来定义const常量,而不是static const int变量或define宏定义。枚举值将在编译时由编译器替换,从而使代码更快。 define宏定义不是命名空间安全的。
  • Prefer verbose argument names in headers. Qt Creator will show the argument names in their completion box. It will look better in the documentation.(???)

从模板或工具类继承

从模板或工具类继承有以下潜在陷阱:

  • 析构函数不是虚函数,这可能导致内存泄漏。

  • 这些符号不会导出(并且大部分是内联的),这可能导致符号冲突。

例如,库A具有类Q_EXPORT X: public QList {}; 库B具有类Q_EXPORT Y: public QList {};。突然,QList符号从两个库中导出,这产生了冲突。

继承与聚合

  • 如果存在明确的is-a关系,请使用继承。
  • 使用聚合来重复使用正交构建基础类。
  • 如果可以选择,则优先选择聚合而不是继承。

公共头文件的约定

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

  • 没有C类型强制转换(-Wold-style-cast)。请使用static_cast,const_cast或reinterpret_cast,对于基本类型,使用构造函数形式:int(a)代替(int)a。 有关更多信息,请参见Casting。

  • 没有浮点数比较(-Wfloat-equal)。使用qFuzzyCompare来比较值与差值。使用qIsNull来检查浮点数是否为二进制0,而不是将其与0.0进行比较,或者,最好将此类代码移至实现文件中。

  • 不要在子类中隐藏虚函数({-Woverloaded-virtual})。如果基类A具有虚函数int val(),子类B具有相同名称的重载,int val(int x),则类A的val函数将被隐藏。使用using关键字使其再次可见,并为被破坏的编译器添加以下愚蠢的解决方法:

      class B: public A
    {
    #ifdef Q_NO_USING_KEYWORD
    inline int val() { return A::val(); }
    #else
    using A::val;
    #endif
    };
  • 不要同名局部变量遮蔽其他外部变量(-Wshadow)。

  • 如果可能的话,避免this->x = x;。

  • 变量与在类中声明的函数不要同名。

  • 为了提高代码的可读性,始终先检查预处理变量是否已定义,后获取变量值(-Wundef)。

      #if defined(Foo) && Foo == 0
    
        -NOT-
    
      #if Foo == 0
    
      -NOT-
    
      #if Foo - 0 == 0
  • 使用#defined运算符检查预处理变量时,请始终把变量放在括号中。

      #if defined(Foo)
    
      -NOT-
    
      #if defined Foo

类成员名称

我们使用"m_"前缀约定,但公有结构化成员除外(通常在*Private类中,并且真正的公有结构化成员很少见)。dq指针不受"m_"规则的限制。

d指针("Pimpls")被命名为“ d”,而不是"m_d"。Foo类中d指针的类型为FooPrivate *,其中FooPrivate所在命名空间与Foo相同,或者,如果Foo被导出,则在相应的{Internal}命名空间。

如果需要(例如,当私有对象需要发出对应类的信号时),FooPrivate可以成为Foo的友元。

如果私有类需要反向引用其对应的类,则将指针命名为q,其类型为Foo *。(与Qt中的约定相同:"q"看起来像倒置的"d"。)

不要使用智能指针来守卫d指针,因为它会增加编译和链接时间开销,并创建带有更多符号的臃肿对象代码,这会减慢调试器的启动速度:

  ############### bar.h

  #include <QScopedPointer>
//#include <memory> struct BarPrivate; struct Bar
{
Bar();
~Bar();
int value() const; QScopedPointer<BarPrivate> d;
//std::unique_ptr<BarPrivate> d;
}; ############### bar.cpp #include "bar.h" struct BarPrivate { BarPrivate() : i(23) {} int i; }; Bar::Bar() : d(new BarPrivate) {} Bar::~Bar() {} int Bar::value() const { return d->i; } ############### baruser.cpp #include "bar.h" int barUser() { Bar b; return b.value(); } ############### baz.h struct BazPrivate; struct Baz
{
Baz();
~Baz();
int value() const; BazPrivate *d;
}; ############### baz.cpp #include "baz.h" struct BazPrivate { BazPrivate() : i(23) {} int i; }; Baz::Baz() : d(new BazPrivate) {} Baz::~Baz() { delete d; } int Baz::value() const { return d->i; } ############### bazuser.cpp #include "baz.h" int bazUser() { Baz b; return b.value(); } ############### main.cpp int barUser();
int bazUser(); int main() { return barUser() + bazUser(); }

结果:

  Object file size:

   14428 bar.o
4744 baz.o 8508 baruser.o
2952 bazuser.o Symbols in bar.o: 00000000 W _ZN3Foo10BarPrivateC1Ev
00000036 T _ZN3Foo3BarC1Ev
00000000 T _ZN3Foo3BarC2Ev
00000080 T _ZN3Foo3BarD1Ev
0000006c T _ZN3Foo3BarD2Ev
00000000 W _ZN14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEEC1EPS2_
00000000 W _ZN14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEED1Ev
00000000 W _ZN21QScopedPointerDeleterIN3Foo10BarPrivateEE7cleanupEPS2_
00000000 W _ZN7qt_noopEv
U _ZN9qt_assertEPKcS1_i
00000094 T _ZNK3Foo3Bar5valueEv
00000000 W _ZNK14QScopedPointerIN3Foo10BarPrivateENS_21QScopedPointerDeleterIS2_EEEptEv
U _ZdlPv
U _Znwj
U __gxx_personality_v0 Symbols in baz.o: 00000000 W _ZN3Foo10BazPrivateC1Ev
0000002c T _ZN3Foo3BazC1Ev
00000000 T _ZN3Foo3BazC2Ev
0000006e T _ZN3Foo3BazD1Ev
00000058 T _ZN3Foo3BazD2Ev
00000084 T _ZNK3Foo3Baz5valueEv
U _ZdlPv
U _Znwj
U __gxx_personality_v0

文档

文档是从源文件和头文件生成的。 您编写文档是为了其他开发人员,而不是自己。 在头文件中,编写接口文档。 也就是说,函数是做什么的,而不是怎么实现的。

在.cpp文件中,如果函数实现不明显,则可以编写相关的文档。


原创造福大家,共享改变世界

献出一片爱心,温暖作者心灵


qt creator源码全方面分析(2-1)的更多相关文章

  1. qt creator源码全方面分析(3-3)

    目录 qtcreatordata.pri 定义stripStaticBase替换函数 设置自定义编译和安装 QMAKE_EXTRA_COMPILERS Adding Compilers 示例1 示例2 ...

  2. qt creator源码全方面分析(3-5)

    目录 qtcreatorlibrary.pri 使用实例 上半部 下半部 结果 qtcreatorlibrary.pri 上一章节,我们介绍了src.pro,这里乘此机会,把src目录下的所有项目文件 ...

  3. qt creator源码全方面分析(0)

    本人主攻C++和Qt. 上两天刚研究完Qt install framework(IFW)应用程序安装框架. google没发现有正儿八经的官方文档的翻译,我就进行了翻译哈!! 系列文章具体见:http ...

  4. qt creator源码全方面分析(4-0)

    Qt系统 Qt Creator源码是在Qt对象和框架基础下写的,因此,阅读Qt Creator源码,你首先对Qt得有一定的了解. Qt Core Qt Core特征: The Meta-Object ...

  5. qt creator源码全方面分析(4-2)

    目录 global头文件 global.h xx.h global头文件 插件的本质就是动态链接库,对于库,需要导出符号,供用户导入使用.在qt creator的源码中,存在固定的导入导出模式. gl ...

  6. qt creator源码全方面分析(4-5)

    目录 Qt中的字符串 QLatinString 详细介绍 源码 小结 QStringLiteral(str) 详细介绍 源码 小结 Qt中的字符串 Qt中处理字符串最常用的肯定是QString,但是在 ...

  7. qt creator源码全方面分析(4-6)

    目录 Qt插件基础 Qt插件基础 我们知道Qt Creator源码是基于插件架构的,那么我们先来介绍下插件基础知识. 相关内容如下: How to Create Qt Plugins [ - Defi ...

  8. qt creator源码全方面分析(3-2)

    目录 qtcreator.pri 判断重复包含 定义版本信息 VERSION 定义IDE名称 启用C++14 CONFIG 自定义函数 Replace Functions Test Functions ...

  9. qt creator源码全方面分析(2-7)

    目录 Completing Code 补全代码片段 编辑代码片段 添加和编辑片段 删除片段 重置片段 补全Nim代码 Completing Code 在编写代码时,Qt Creator建议使用属性,I ...

  10. qt creator源码全方面分析(2-10-1)

    目录 Getting and Building Qt Creator 获取Qt 获取和构建Qt Creator Getting and Building Qt Creator 待办事项:应该对此进行扩 ...

随机推荐

  1. tomcat日志传参乱码问题

    问题:      在centos系统下,tomcat8.0.36控制台日志打印会出现中文乱码. 解决方案:      在catalina.sh里加上 JAVA_OPTS="-Dfile.en ...

  2. 轻松搭建基于 SpringBoot + Vue 的 Web 商城应用

    背景介绍 首先介绍下在本文出现的几个比较重要的概念: 函数计算(Function Compute): 函数计算是一个事件驱动的服务,通过函数计算,用户无需管理服务器等运行情况,只需编写代码并上传.函数 ...

  3. 【题解】LOJ2759. 「JOI 2014 Final」飞天鼠(最短路)

    [题解]LOJ2759. 「JOI 2014 Final」飞天鼠(最短路) 考虑最终答案的构成,一定是由很多飞行+一些上升+一些下降构成. 由于在任何一个点上升或者下降代价是一样的,所以: 对于上升操 ...

  4. sql函数实用——字符函数(sqlserver与mysql对比)

    1.获取长度 sqlserver写法:关键字:len()    获取参数的字符数量 select  Len('aksjdhh')    输出结果 7 select len('张无忌ooo')   输出 ...

  5. 公子奇带你进入Java8流的世界(二)

    在上一篇中我们带领大家简单的了解流的概念及使用场景,本节我们就来好好的介绍流的常见用法. 一.筛选和切片 对于一串流,我们有时需要取出我们需要的流中某些元素,主要是通过谓词筛选.看代码: 首先定义一个 ...

  6. (01)hibernate框架环境搭建及测试

    ---恢复内容开始--- 1.创建javaweb项目 2.导包 hibernate包 hibernate\lib\required\*.jar 数据库驱动包 mysql-connector-java- ...

  7. Redis-CAP定理和BASE理论(二)

    CAP理论概述 1998 年来自柏克莱加州大学的计算机科学家 埃里克.布鲁尔(Eric Brewer) 提出分布式系统的三个基本指标:Consistency(一致性).Availability(可用性 ...

  8. java小项目之:象棋,羡慕你们有对象的!

    象棋,是我国传统棋类益智游戏,在中国有着悠久的历史,属于二人对抗性游戏的一种,由于用具简单,趣味性强,成为流行极为广泛的棋艺活动.中国象棋是中国棋文化也是中华民族的文化瑰宝. 象棋还有很多口诀,这是最 ...

  9. DWVA-关于反射型xss的漏洞详解<xss reflected>

    反射型xss low级别 代码如下: <?php header ("X-XSS-Protection: 0"); // Is there any input? if( arr ...

  10. 重拾c++第二天(4):复合类型

    1.定义:种类 数组名[元素个数] = {元素1,...,元素n} ,或者直接赋值:数组名[元素位置] = 值; 2.部分初始化,其他全为0,可以就定义一个0,这样得到0数组(或者就一个{},别的啥也 ...