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

1059 篇文章 284 订阅

模版是C++中的一种内置机制,它运行编辑器根据传入的参数的类型动态生成代码。因此,模块对于框架创建者来说是非常有趣的,我们在Qt的很多地方都使用了高级模板,但是也有一些限制:有些地方可以很容器的用模板来表达, 模版是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是最著名的。它们以语法规范作为输入,并生成实现状态机的代码。代码生成器的替代品是黑编译器、专有语言或图形编程工具,它们带有单向对话框或向导,在设计时(而不是编译时)生成晦涩的代码。我们没有将客户锁定在专有的c++编译器或特定的集成开发环境中,而是允许他们使用他们喜欢的任何工具。我们不强迫程序员将生成的代码添加到源代码存储库中,而是鼓励他们将我们的工具添加到他们的构建系统中:更干净、更安全、更符合UNIX精神。

GUI是动态的

C++是一种标准化的,强大的,精心设计的通用语言。它是唯一一种被广泛用于各种软件项目的语言,从整个操作系统、数据库服务器、高端图形应用程序到常见的桌面应用程序。C++成功的关键之一是它的可扩展语言设计,它关注于最大性能和最小内存消耗,同时仍然保存ANSI C的兼容性

尽管有这些优点,但也有一些缺点。对于C ++,在基于组件的图形用户界面编程方面,静态对象模型明显优于Objective C的动态消息传递方法。适合高端数据库服务器或操作系统的设计不一定适合GUI前端。有了moc,我们把这个缺点变成了优点,并增加了所需的灵活性,以满足安全高效的图形用户界面编程的挑战。

我们的方法远远超出了使用模板所能做到的任何事情。例如,我们可以有对象属性。我们还可以有重载信号和槽,在重载是关键概念的语言中编程时,这感觉很自然。我们的信号为类实例的大小添加了零字节,这意味着我们可以在不破坏二进制兼容性的情况下添加新信号。

另一个好处是我们可以在运行时探索对象的信号和插槽。我们可以使用类型安全的按名调用(call-by-name)来建立连接,而不需要知道所连接对象的确切类型。这对于基于模板的解决方案是不可能的。这种运行时自省带来了新的可能性,例如从Qt设计器的XML UI文件生成并连接的gui。

调用性能并不是一切

Qt的信号和插槽实现不如基于模板的解决方案快。发出一个信号的成本大约是使用普通模板实现的四个普通函数调用的成本,而Qt所需要的工作量大约相当于十个函数调用。这并不奇怪,因为Qt机制包括通用的封送程序、内省、不同线程之间的队列调用以及最终的脚本性。它不依赖于过度的内联和代码扩展,而且它提供了无与伦比的运行时安全性。Qt的迭代器是安全的,而那些更快的基于模板的系统则不是。即使在向几个接收器发射信号的过程中,这些接收器也可以被安全地删除,而不会导致程序崩溃。如果没有这个安全措施,你的应用程序最终会因为一个难以调试的对空闲内存的读写错误而崩溃。

然而,基于模板的解决方案难道不能提高使用信号和插槽的应用程序的性能吗?虽然Qt确实增加了通过信号调用插槽的开销,但调用的开销只占插槽总开销的一小部分。对Qt信号和插槽系统的基准测试通常使用空插槽。一旦您在槽中做了任何有用的事情,例如一些简单的字符串操作,调用开销就可以忽略不计了。Qt的系统是如此的优化,以至于任何需要operator new或delete的操作(例如,字符串操作或从模板容器插入/删除某些东西)都比发出信号要昂贵得多。

另外:如果在性能关键任务的紧密内部循环中有一个信号和插槽连接,并且您认为这个连接是瓶颈,请考虑使用标准的侦听器接口模式,而不是使用信号和插槽。在这种情况下,您可能只需要一个1:1连接。例如,如果您有一个从网络下载数据的对象,那么使用信号来指示请求的数据到达是一种非常明智的设计。但是,如果您需要将每个字节一个一个地发送给消费者,那么请使用侦听器接口,而不是信号和插槽。

没有限制

因为我们有信号和插槽的moc,所以我们可以向其添加模板无法完成的其他有用的事情。其中包括通过生成的tr()函数进行限定范围的转换,以及带有内省和扩展运行时类型信息的高级属性系统。属性系统本身就是一个很大的优势:

  • 如果没有功能强大且自省的属性系统,那么像Qt Designer这样的功能强大且通用的用户界面设计工具将很难编写(即使不是不可能)。
  • 我们还提供了一个动态qobject_cast<T>()机制,它不依赖于系统的RTTI,因此不共享其限制。我们使用它安全地从动态加载的组件查询接口。
  • 另一个应用程序域是动态元对象。例如,我们可以使用ActiveX组件,并在运行时在其周围创建一个元对象。或者我们可以通过导出Qt组件的元对象将其导出为ActiveX组件。您不能使用模板来做这两种事情。

C ++moc本质上为我们提供了Objective-C或Java运行时环境的灵活性,同时保持了C ++独特的性能和可伸缩性优势。这就是使Qt成为我们今天拥有的灵活舒适的工具的原因。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值