Qt元对象系统(Meta-Object)(三)、为什么Qt将Moc用于信号和插槽

目录

  • 为什么Qt将Moc用于信号和槽?
      • 语法的重要性
      • 代码生成器很好用
      • GUI是动态的
      • 调用性能并非一切
      • 没有限制

为什么Qt将Moc用于信号和槽?

  模板是C ++中的内置机制,允许编译器动态生成代码,具体取决于传递的参数类型。因此,模板对于创建框架的人来说非常有趣,我们在Qt的许多地方都使用高级模板。但是,存在一些局限性:你可以使用模板轻松表达某些内容,但是有些内容是无法用模板表达的。一个通用的vector容器类很容易表示,即使指针类型需要部分特化,而一个基于以字符串形式给出的XML描述设置图形用户界面的函数就不能用模板来表示。而且在它们之间有一个灰色区域。你可以使用模板来修改一些东西,当然代价是代码的大小,可读性,可移植性,可用性,可扩展性,稳健性和最终设计美感。模板和C预处理器都可以延伸,以实现令人难以置信的智能和令人难以置信的事情。但仅仅因为这些事情可以做到,并不一定意味着它们是正确的设计选择。遗憾的是,代码并不是要在书籍中发布,而是在真实世界的操作系统上使用真实编译器进行编译。
以下是Qt使用moc的一些原因:

语法的重要性

  语法不仅仅是糖:我们用来表达算法的语法会显著地影响代码的可读性和可维护性。 用于Qt信号和槽的语法在实践中证明非常成功。 语法直观,易于使用且易于阅读。 学习Qt的人发现语法有助于他们理解和用信号和槽 - 尽管它具有高度抽象性和通用性。 这有助于程序员从一开始就知道如何去设计,甚至无需考虑设计模式。

代码生成器很好用

  Qt的moc(元对象编译器)提供了一种超越编译语言功能的简洁方法。它通过生成可以由任何标准C ++编译器编译的附加C ++代码来实现。 moc读取C ++源文件。如果它找到一个或多个包含Q_OBJECT宏的类声明,它会生成另一个C ++源文件,其中包含这些类的元对象代码。由moc生成的C ++源文件必须编译并与类的实现链接(或者它可以#included到类的源文件中)。通常,moc不是手动调用,而是由构建系统自动调用,因此程序员无需额外工作。
  moc不是Qt使用的唯一代码生成器。另一个突出的例子是uic(用户界面编译器)。它接受XML中保存的用户界面描述信息,并创建用于设置界面的c++代码。在Qt之外,代码生成器也很常见。以rpc和idl为例,它使程序或对象能够通过进程或机器边界进行通信。或者各种各样的扫描仪和解析器生成器,其中lex和yacc是最知名的。它们将语法规范作为输入并生成实现状态机的代码。代码生成器的替代方案是hacked compilers,专有语言或图形编程工具,其中包含单向对话框或向导,这些对话框或向导在设计时而不是编译时生成模糊代码。我们不是将客户锁定在专有的C ++编译器或特定的集成开发环境中,而是让他们使用他们喜欢的任何工具。我们鼓励他们将我们的工具添加到他们的构建系统中,而不是强迫程序员将生成的代码添加到源代码库中:更清晰,更安全,更符合UNIX的精神。

GUI是动态的

  C ++是一种标准化,功能强大且精心设计的通用语言。它是唯一一种在如此广泛的软件项目中使用的语言,涵盖了从整个操作系统、数据库服务器、高端图形应用程序到普通桌面应用程序的各种应用程序。 C ++成功的关键之一是其可扩展的语言设计,专注于最大性能和最小内存消耗,同时仍保持ANSI C兼容性。
  尽管有这些优点,也有一些缺点。对于C ++,当涉及基于组件的图形用户界面编程时,静态对象模型明显不利于Objective C的动态消息传递方法。对高端数据库服务器或操作系统有好处的东西不一定是GUI前端的正确设计选择。使用moc,我们已将这一缺点转化为优势,并增加了应对安全高效的图形用户界面编程挑战所需的灵活性。
  我们的方法远远超出了您使用模板所能做的任何事情。例如,我们可以拥有对象属性。我们还可以重载信号和槽,我们的信号将零字节添加到类实例的大小,这意味着我们可以添加新信号而不会破坏二进制兼容性。
  另一个好处是我们可以在运行时探索对象的信号和槽。我们可以使用类型安全的按名称调用来建立连接,而不需要知道正在连接的对象的确切类型。使用基于模板的解决方案是不可能的。这种运行时内省开辟了新的可能性,例如从Qt Designer的XML UI文件生成和连接的GUI。

调用性能并非一切

  Qt的信号和槽实现没有基于模板的解决方案那么快。虽然使用通用模板实现发出一个信号的开销大约是4个普通函数调用的开销,但是Qt需要的工作量相当于10个函数调用。这并不奇怪,因为Qt机制包括一个通用的编组器、内省、不同线程之间的排队调用,以及最终的脚本功能。它不依赖于过多的内联和代码扩展,并且提供了无与伦比的运行时安全性。Qt的迭代器是安全的,而那些更快的基于模板的系统则不是。即使在向多个接收器发送信号的过程中,也可以安全地删除这些接收器,而不会导致程序崩溃。如果没有这种安全性,您的应用程序最终将崩溃,出现难以调试的内存读或写错误。
  然而,基于模板的解决方案不能提高使用信号和槽的应用程序的性能吗?虽然Qt确实给通过信号调用槽的成本增加了少量开销,但是调用的成本只占槽整个成本的一小部分。对Qt的信号和槽系统进行基准测试通常使用空槽。只要在槽中执行一些有用的操作(例如一些简单的字符串操作),调用开销就可以忽略不计。Qt的系统非常优化,任何需要操作符new或delete(例如,字符串操作或从模板容器中插入/删除某些内容)的操作都比发出信号要昂贵得多。
  旁白:如果您在性能关键任务的紧密内部循环中有一个信号和槽连接,并且您将此连接视为瓶颈,请考虑使用标准的listener-interface模式,而不是信号和槽。在这种情况下,您可能只需要1:1的连接。例如,如果您有一个从网络下载数据的对象,那么使用一个信号来表示请求的数据已经到达,这是一个非常明智的设计。但是,如果您需要将每个字节逐个发送给消费者,请使用侦听器接口,而不是信号和槽。

没有限制

  因为我们有支持信号和槽的moc,所以我们可以向它添加其他有用的东西,这是模板做不到的。其中包括通过生成的tr()函数进行转换,以及带有自省和扩展运行时类型信息的高级属性系统。属性系统本身就是一个巨大的优势:如果没有一个功能强大且内省的属性系统,像Qt Designer这样功能强大且通用的用户界面设计工具将很难编写(如果不是不可能的话)。但这并没有结束。我们还提供了一个动态qobject_cast()机制,该机制不依赖于系统的RTTI,因此没有它的限制。我们使用它来安全地查询动态加载的组件的接口。另一个应用程序域是动态元对象。例如,我们可以使用ActiveX组件,并在运行时围绕它创建一个元对象。或者我们可以通过导出Qt的元对象将Qt组件导出为ActiveX组件。您不能使用模板做这两件事。
  使用moc的c++本质上为我们提供了Objective-C或Java运行时环境的灵活性,同时保持了c++独特的性能和可伸缩性优势。正是它使Qt成为我们今天拥有的灵活而舒适的工具。

你可能感兴趣的:(qt)