Qt之qobject_cast实现
使用背景
qobject_cast与C++提供的static_cast、dynamic_cast类似,用于实现类的动态转换。
/*qobject_cast函数*/
DestType* qobject_cast<DestType*>(QObject *p);
使用该函数有两个前提条件:
- 需要转换的类需要继承自QObject
- 类需要声明Q_OBJECT宏
qobject_cast()函数的行为类似于标准c++ dynamic_cast(),其优点是它不需要RTTI支持,并且可以跨动态库边界工作。它试图将其实参强制转换为尖括号中指定的指针类型,如果对象的类型正确(在运行时确定),则返回一个非零指针,如果对象的类型不兼容则返回0。
实现原理
Q_OBJECT宏
#define Q_OBJECT \
public: \
QT_WARNING_PUSH \
Q_OBJECT_NO_OVERRIDE_WARNING \
static const QMetaObject staticMetaObject; \
virtual const QMetaObject *metaObject() const; \
virtual void *qt_metacast(const char *); \
virtual int qt_metacall(QMetaObject::Call, int, void **); \
QT_TR_FUNCTIONS \
private: \
Q_OBJECT_NO_ATTRIBUTES_WARNING \
Q_DECL_HIDDEN_STATIC_METACALL static void qt_static_metacall(QObject *, QMetaObject::Call, int, void **); \
QT_WARNING_POP \
struct QPrivateSignal {}; \
QT_ANNOTATE_CLASS(qt_qobject, "")
Q_OBJECT宏所代指的代码如上所示,其中在所有启用Q_OBJECT宏的类中都新建了一个静态的QMetaObject实例staticMetaObject
。
qmake对包含Q_OBJECT宏的文件会生成一个moc文件,moc文件中对staticMetaObject
的初始化代码如下。
QT_INIT_METAOBJECT const QMetaObject MyDialog::staticMetaObject = { {
//这里将父类的staticMetaObject对象指针存入当前类superdata形成继承树
QMetaObject::SuperData::link<QDialog::staticMetaObject>(),
qt_meta_stringdata_MyDialog.data,
qt_meta_data_MyDialog,
qt_static_metacall,
nullptr,
nullptr
} };
qobject_cast函数
template <class T>
inline T qobject_cast(QObject *object)
{
//获取输入模板类
typedef typename std::remove_cv<typename std::remove_pointer<T>::type>::type ObjType;
Q_STATIC_ASSERT_X(QtPrivate::HasQ_OBJECT_Macro<ObjType>::Value,
"qobject_cast requires the type to have a Q_OBJECT macro");
//通过调用模板类的静态成员变量的cast函数,函数的传入参数为需要转换的指针
return static_cast<T>(ObjType::staticMetaObject.cast(object));
}
下面是QMetaObject类的cast函数
QObject *QMetaObject::cast(QObject *obj) const
{
// ### Qt 6: inline
return const_cast<QObject*>(cast(const_cast<const QObject*>(obj)));
}
const QObject *QMetaObject::cast(const QObject *obj) const
{
//判断传入的指针是否继承自模板类
//如果是则传出obj 进行static_cast是安全的
//如果不是传出nullptr
//因此qobject_cast相当于dynamic_cast,是一个安全的转型函数
return (obj && obj->metaObject()->inherits(this)) ? obj : nullptr;
}
bool QMetaObject::inherits(const QMetaObject *metaObject) const noexcept
{
//这里this指针指向的为之前传入模板类的staticMetaObject
const QMetaObject *m = this;
//对输入mentaObject的继承树向前查找,判断是否存在与传入模板类相同的staticMetaObject
//若存在则证明输入类继承自模板类
//d.superdata是在moc文件中完成初始化的
do {
if (metaObject == m)
return true;
} while ((m = m->d.superdata));
return false;
}
总结
- qobject_cast使用需要满足:①继承自QOBject类 ②启用Q_OBJECT宏
- qobject_cast为安全的转型函数,其效率比dynamic_cast要高