QT相关知识

信号与槽

一、信号函数和槽函数
信号和槽机制底层是通过函数间的相互调用实现的。每个信号都可以用函数来表示,称为信号函数;每个槽也可以用函数表示,称为槽函数。如果有对象对信号感兴趣,就使用connect函数绑定来处理这个信号,信号发出时被连接的槽函数会自动被调用。

信号是公有访问函数,可以从任何地方发出,但是建议只从定义信号的类或子类发出。如果多个槽连接到了一个信号,当信号发出时,这些槽函数会按连接的顺序一一执行。信号函数由moc( 元对象编译器,在本文后续章节说明)自动生成并且不能在源文件中实现,且返回类型只能是void。

槽函数是普通的C++函数,可以用一般的方式被调用,也可以将槽函数定义为虚函数,唯一的特点是可以被信号函数连接。槽函数可以通过信号槽连接被任何组件调用,而忽视本身的访问级别。这意味着任意一个类的实例发出的信号可导致不相关的类的实例的私有槽函数被调用。

二、信号槽的连接
通过connect可以将信号和槽函数进行连接,触发信号时就会执行槽函数。大致的过程就是在connect时,会将连接信息保存在ConnectionList中,触发信号时调用列表中的槽函数。更详细的原理可以参考:https://blog.csdn.net/huang12041/article/details/109674187
信号槽的连接方式:
(1)Qt::AutoConnection:默认连接方式,如果信号的发出和接收这个信号的对象同属一个线程,那个工作方式与直连方式相同;否则工作方式与排队方式相同。
(2)Qt::DirectConnection:同步执行槽函数,当信号发送后,相应的槽函数将立即被调用。
(3)Qt::QueuedConnection:当信号发出后,放入信号队列中,等到接收对象所属线程的事件循环取得控制权时才取得该信号,调用相应的槽函数。
(4)Qt::BlockingQueuedConnection:发送信号后发送者所在的线程会处于阻塞状态 ,直到槽函数运行完,此时信号和槽必须在不同的线程中。
(5)Qt::UniqueConnection:不能重复连接相同的信号和槽,可以避免多次触发槽函数,可以和以上几种参数位或使用。
(6)Qt::AutoCompatConnection:兼容老版本QT,工作方式与Qt::AutoConnection一样。

三、注意事项
(1)所有包含信号槽的类必须在类定义的顶部声名Q_OBJECT这个宏,并且该类必须直接或间接继承QObject。
(2)默认情况下,每个建立的连接都会有一个信号发出,重复连接会发出两个信号,除非连接时设置了Qt::UniqueConnection类型。

四、信号槽的优缺点
Qt信号与槽机制降低了Qt对象的耦合度。发出信号的对象无须知道是哪个对象需要接收它发出的信号;同样,槽函数也不需要知道哪些信号关联了自己。但是,同回调函数相比信号和槽机制运行速度慢,这是定位连接对象、安全地遍历所有连接以及以通用方式调用任何参数的开销。

元对象编译器moc

元对象编译器moc(Meta-Object Compiler)是Qt对C++的扩展,在标准编译器编译之前, moc会分析 C++ 源文件。如果头文件中包含了宏 Q_OBJECT,则会生成另外一个 C++ 源文件,其中包含了 Q_OBJECT 宏的实现代码。新的文件是原文件名前面加上 moc_ ,它也会进入编译系统一起参与编译。

一、头文件编译
每一个定义了Q_OBJECT宏或者继承于QObject的类,moc后都从Q_OBJECT宏得到以下一些关键的声明代码:


static const QMetaObject staticMetaObject;
virtual const QMetaObject *metaObject() const;
virtual void *qt_metacast(const char *);
virtual int qt_metacall(QMetaObject::Call, int, void **);
// 私有
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **);

并且在moc_*.cpp文件进行实现,对以上的实现进行分析可以得出:
(1)staticMetaObject:一个结构体,存储了基类staticMetaObject指针、信号槽等数据,另外还有qt_static_metacall函数指针。
(2)metaObject:返回staticMetaObject指针。
(3)qt_metacast:元对象中的字符数据转换。
(4)qt_metacall:元对象调用入口,槽函数调用通过这个函数进行,内部调用qt_static_metacall。
(5)qt_static_metacall:根据函数索引调用槽函数,信号函数也可以当作槽函数一样被调用(信号connect信号的原理)。

moc_*.cpp中还会实现信号函数,如下代码, 当信号发出时会调用QMetaObject::activate函数,此时会先从connectionLists中根据信号序号(以下信号的0、1)取出连接列表。根据connect设置的不同连接类型,进行不同的逻辑处理去调用槽函数。

// SIGNAL 0
void test::signal_xx()
{
    QMetaObject::activate(this, &staticMetaObject, 0, nullptr);
}
 
// SIGNAL 1
void test::signal_xxx(int _t1)
{
    void *_a[] = { nullptr, const_cast<void*>(reinterpret_cast<const void*>(&_t1)) };
    QMetaObject::activate(this, &staticMetaObject, 1, _a);
}

二、关键字宏定义

# define QT_ANNOTATE_ACCESS_SPECIFIER(x)
// 信号都是公有的
# define Q_SIGNALS public QT_ANNOTATE_ACCESS_SPECIFIER(qt_signal)
# define signals Q_SIGNALS

// 槽可以自己指定公有、私有、保护
# define Q_SLOTS QT_ANNOTATE_ACCESS_SPECIFIER(qt_slot)
# define slots Q_SLOTS

// emit没有定义任何内容,和信号一起使用的时候就是调用了信号函数
#define emit

反射机制

Qt的反射机制所依赖的元对象数据,也是moc在编译时产生的。Q_INVOKABLE对应成员函数,Q_PROPERTY对应成员变量,Q_ENUM对应枚举。所有继承于QObject的类可以 返回元对象系统为其生成的metaObject对象,可以通过该对象中的方法获取QMetaMethod、QMetaProperty、QMetaEnum对象,通过这些对象就可以通过方法名称、变量名称、枚举名称直接操作,而不需要知道类的具体定义。

Qt的放射机制无法直接根据类型名称取得对象,在应用时始始终还是需要知道具体的类的具体定义,才能通过类对象拿到metaObject对象。不过可以通过自己的封装达到外部只需要提供类名,也能获取到类的metaObject对象的效果,就是不那么好用。

const QMetaObject *customMetaObject(const QString& classname)
{
	if ("CustomClass1" == name)
	{
		return &CustomClass1::staticMetaObject;
	}
	else if ("CustomClass2" == name)
	{
		return &CustomClass2::staticMetaObject;
	}
}

你可能感兴趣的:(面试八股文,qt,开发语言,c++)