背景:
【qt信号槽-1】槽函数重写问题,qt_metacall和qt_static_metacall
【qt信号槽-2】Qt中窗体继承,槽响应多次执行的解决_qt界面继承 槽函数多次执行
【qt信号槽-3】(QObject::connect: No such slot)的一种解决方法,connect函数qt4/qt5格式,元数据注册
【qt信号槽-4】槽函数不响应不执行的一种原因:ui提升导致重名_qt 按钮不能进入函数
之前写了重写多态的理解,我自认为是一种比较极端,但是容易在使用角度掌握的理解。
之所以开始研究这个概念,是因为遇到了问题,我做了一个比较通用的界面,现在想继承一个并丰富其功能,这需要重写其中一个槽函数,而这个槽函数是有其内部控件触发的。
按照重写函数的经验,我以为还需要显式调用父类的函数功能,但实际上它是自动实现的,这就超出我认知了,所以尝试跟踪代码看看。
分析:
汇编部分比较麻烦,真的不擅长,只看高级语言部分。
源头在于发信号:
QMetaObject::activate(this, &staticMetaObject, 1, _a);
都会用到一个staticMetaObject结构体,而这个结构体定义是这样的:
const QMetaObject HQ_Base_Db_Grid::staticMetaObject = {
{ &QTableWidget::staticMetaObject, qt_meta_stringdata_HQ_Base_Db_Grid.data,
qt_meta_data_HQ_Base_Db_Grid, qt_static_metacall, nullptr, nullptr}
};
其中有我自己定义的类名,不重要。重点是qt_static_metacall函数的调用。也就是说,只要发信号,肯定调用qt_static_metacall。而qt_static_metacall会调用本类当中的槽函数。比如某个界面中各种控件的响应,都是这样开始的。
再看一个子类的moc代码:
int Config_User_Users::qt_metacall(QMetaObject::Call _c, int _id, void **_a)
{
_id = Base_Grid_Single::qt_metacall(_c, _id, _a);
if (_id < 0)
return _id;
if (_c == QMetaObject::InvokeMetaMethod) {
if (_id < 1)
qt_static_metacall(this, _c, _id, _a);
_id -= 1;
} else if (_c == QMetaObject::RegisterMethodArgumentMetaType) {
if (_id < 1)
*reinterpret_cast<int*>(_a[0]) = -1;
_id -= 1;
}
return _id;
}
很明显,它显式地调用了父类的qt_metacall函数。但执行子类这段代码的时候,它并不会跳转到下面的qt_static_metacall函数那一句。
接着,在父类的qt_metacall函数中,跳转到了父类的qt_static_metacall函数,于是执行了父类的槽函数。而后子类才又调用了自己的qt_static_metacall函数,来执行自己的槽函数。
结论:
当某个类没有重写父类槽函数时,它会直接调用qt_static_metacall函数,进而执行自己的槽函数。
当重写了父类的槽函数时,它会调用qt_metacall函数,然后显式调用父类的qt_static_metacall函数,从而执行父类的槽函数。而且这个过程是一直递归到基类的,所有的槽函数按照辈分执行。最后子类再调用自己的qt_static_metacall函数,从而执行自己的槽函数。
所以我能否这样理解?
qt的槽机制中,为重写槽函数提供了更智能的方法,不用程序员再显式调用父类槽函数。相当于槽函数可以像类一样继承。
我想,其实验证一下也容易,重写系统某个槽函数,比如事件过滤器,试试就知道了。