QApplication 和事件循环

QApplication 和事件循环

观察者模式
在编写事件驱动的程序中,GUI 视图需要对数据模型对象的的状态变化做出响应,以便它们可以显示最先的消息。当任何数据模型对象发生状态改变时,就需要一种间接的方式来提醒观察者。观察者就是一些正在监听(并响应)状态变化的对象。使用这种消息传递机制的设计模式就称为观察者模式

  1. 允许实体观察者类与实体类之间解耦
  2. 支持广播风格,一对多的通信
  3. 所采用的从主体向观察者发送消息的机制完全有主体的基类给定。

Qt 的 QEven 类封装了底层事件的概念。QEvent 类是若干特定的事件类的基类,例如:QActionEvent,QCloseEvent 等。
QEvent 对象可以由窗口系统创建以响应用户的动作(例如:QMouseEvent),或按照指定的时间间隔( QTimeEvent )完成创建。成员函数 type() 会返回一个枚举,以区分不同种类的各式事件(如:KeyPress、MouseButtonClick、MouseMove等)。

事件循环是一个程序结构,它能够将事件划分优先级,排队并分派给一些对象。事件循环是一个程序结构,它能够将事件划分优先级,排队并分派给一些对象。编写一个基于事件的应用程序就是实现由函数组成的被动接口,且这些函数仅仅在某些特定事件发生时才会得到调用。
事件循环通常一直运行,直到遇到某个终止事件。
一个典型的Qt程序会创建对象,连接各个对象,然后再告诉应用程序开始exec()。在运行时,应用程序就进入了事件循环。

int main(int argc, char *argv[])
{
    QApplication app(argc,argv);
    MainWindow w;
    w.show();
    return app.exec();
}

Q_OBJECT 和 moc

QObject 支持一些普通 C++ 对象所没有的特性:

  • 信号和槽
  • 元对象、原属性和元方法
  • qobject_cast

元对象编译器,即 moc,会针对每个使用 Q_OBJECT 宏的 QObject 派生类生成额外的函数,生成的代码可以在名称为 moc_filename.cpp 的文件中找到。上面的某些特性必须使用经过元对象编译器编译后的代码才能实现。
这就意味着,当 moc 无法发现或处理工程中某个类时,编译器或者链接器就会报出一些莫名其妙,含糊不清的错误。为了保证 moc 能够处理所编写的全部 QObject 派生类,下面是编写 C++ 代码和 qmake 工程文件时应该遵守的一些指导原则:

  • 每个类的定义都应该放在对应的 .h 文件中
  • 每个类的实现都应该放在对应的 .cpp 文件中
  • 为了避免头文件的多次包含,头文件应该使用#ifndef避免多次包含
  • 每个 .cpp 源文件都应当列举在工程(.pro)文件的 SOURCES 变量中,否则它将不会被编译
  • 每个头文件都应当列举在工程文件HEADERS变量中。否则,moc将不会对其进行预处理。 [在使用 Q_OBJECT时,工程文件必须要有 HEADERS 的变量]
  • Q_OBJECT 宏必须出现在每个 QObject 派生类定义的头文件中,以便让 moc 知道要为其生成代码。

注意
因为每个 Q_OBJECT 宏都会使用 moc 来产生代码。moc 编译器的代码只会从 QObject 类派生一次。更进一步的说,QObject 类应该是其基类列表中的第一个基类。如果在实际应用中不小心多次继承了 QObject,或者它不是继承列表中的第一个基类,那么就有可能从 moc 生成的代码中发现一些非常奇怪的错误。

注意
如果定义了一个 QObject 派生类,构建了一个应用程序,然后才意识到需要在类的定义中添加 Q_OBJECT 宏时,而且是在该工程使用了旧的 Makefile 文件之后添加的。此时必须使用 qmake 重新生成 Makefile。通常执行 Clean rebuild 命令并不能解决这种问题。

信号和槽

信号是类定义中给出类似于 void 函数声明的一种消息。它有参数列表但没有函数体。信号。
槽类似于普通的成员函数,与信号通过 connect 函数进行连接。

一个对象的信号可以与一个或多个对象的的槽相连接,前提是这些对象存在并且信号和槽的参数列表都是兼容的在 Qt 中,信号的参数一定等于槽的参数,也可以多于,但多于的参数将被忽略。连接的语法为:


bool QObject::connect(senderQObjectPtr,
	SIGNAL(signalName(argumentList)),
	receiverQObjectPtr,
	SLOT(slotName(argumentList)),
	optionalConnectionType
	) ;

函数原型为:


bool QObject::connect (
 const QObject * sender, const char * signal,
  const QObject * receiver, const char * method,
   Qt::ConnectionType type = Qt::AutoConnection ) [static]

提示
如果有多个信号连接到同一个槽上且需要知道是哪个 QObject 对象发送的信号,则可以在该槽中调用 sender() ,它会返回一个指向那个发送信号对象的指针。

栈还是堆
一般情况下,没有父对象的 QObject 应当在栈上创建*【此时的引用计数等一些和内存有关的玩意由Qt管理】,或者定义成另一个类的子对象【此时内存由父对象管理】*。有父对象的 QObject 不应当在栈上创建。因为那样的话,它有可能会被删除两次。在堆上创建的所有 QObject 都应当是有父对象的,或者是由其它对象进行管理的。例如下面的错误案例:

QObject *obj = new QObject();
Person child(",",obj);

delete obj;            	  1

此时的 child 会被释放两次。

注意
不推荐直接使用 delete 来直接删除 QObject 。在带有事件循环的程序中,最好是利用 QObject::deleteLater() 来删除 QObject 。这样做,可以在应用程序处理事件并在当前槽返回之后就安排该对象的销毁。
上面的 1 处代码改为 obj->deleteLater(), child就不会释放两次

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值