Qt內置類型識別系統的使用方法
QObject *obj = new QPushButton("OK");
//判斷obj是否是QPushButton類
bool isPushButton =
obj->metaObject()->className() == QStringLiteral("QPushButton");
cout << isPushButton << endl;
//判斷obj是否是QAbstractButton的子類
bool isChildClass = obj->inherits("QAbstractButton");
cout << isChildClass << endl;
//類型轉換
//Qt類型轉換和C++類型轉換的區別:
//qobject_cast是安全的,類型不符是返回nullptr
//static_cast是不安全的,即使類型不符,也可以強轉,但是繼續使用就可能發生異常
//QObject通過QMetaObject記錄了類型信息,因此可以判斷類型是否匹配,而C++轉換則不可以
QPushButton *button = qobject_cast<QPushButton *>(obj);
cout << button->text().toStdString() << endl;
//錯誤示例:用static_cast進行強轉,即使類型錯誤也能成功,但是調用text()方法是就會報錯
QLabel *label = static_cast<QLabel *>(obj);
cout << label->text().toStdString() << endl;
用原生C++代碼實現Qt的類型識別系統
我們分別定義MObject,MMetaObject類,來模擬Qt的QObject,QMetaObject
#include <iostream>
using namespace std;
//模擬QMetaObject
class MMetaObject {
public:
//類名
string className;
//類大小
size_t classSize;
//基類
MMetaObject *baseClass;
//判斷是否繼承自其它類
bool extendsFrom(MMetaObject *parentClass) {
//如果parentClass即是當前類,也視爲繼承
if (parentClass == this) return true;
//一直向上取基類元對象,如果相等,則說明是其子類
MMetaObject *p = this;
while (p->baseClass) {
p = p->baseClass;
if (p == parentClass) return true;
}
//所有基類元對象都不相等,則說明不是其子類
return false;
}
};
//模擬QObject
class MObject {
public:
static MMetaObject *staticMetaObject;
//獲取當前類的元對象
virtual MMetaObject *metaObject() {
return MObject::staticMetaObject;
}
//獲取類名
string className() {
return metaObject()->className;
}
//判斷是否繼承自其它類
bool extendsFrom(MMetaObject *parentClass) {
return metaObject()->extendsFrom(parentClass);
}
};
//初始化MObject的靜態元對象
MMetaObject *MObject::staticMetaObject =
new MMetaObject { "MObject", sizeof(MMetaObject), nullptr };
//定義一個Animal類
class Animal : public MObject {
public:
static MMetaObject *staticMetaObject;
virtual MMetaObject *metaObject() {
return Animal::staticMetaObject;
}
};
//初始化Animal的靜態元對象
MMetaObject *Animal::staticMetaObject =
new MMetaObject { "Animal", sizeof(Animal), MObject::staticMetaObject };
//定義一個Cat類
class Cat : public Animal {
public:
static MMetaObject *staticMetaObject;
virtual MMetaObject *metaObject() {
return Cat::staticMetaObject;
}
};
//初始化Cat的靜態元對象
MMetaObject *Cat::staticMetaObject =
new MMetaObject { "Cat", sizeof(Cat), Animal::staticMetaObject };
int main() {
Cat *cat = new Cat();
string className = cat->className();
bool extendFromAnimal = cat->extendsFrom(Animal::staticMetaObject);
cout << className << endl;
cout << extendFromAnimal << endl;
return 0;
}
通過自定義宏來封裝簡化代碼
到了上一步我們可以看到,只要定義一個類,繼承MObject,初始化staticMetaObject,就可以使用所有的元對象功能了
我們可以通過自定義宏,來將初始化staticMetaObject這部分代碼也省掉
//定義宏:自動聲明MMetaObject
#define M_OBJECT_DECLARE(class_name) \
public: \
static MMetaObject *staticMetaObject; \
virtual MMetaObject *metaObject() { \
return class_name::staticMetaObject; \
}
//定義宏:自動創建MMetaObject
#define M_OBJECT_IMPLEMENT(class_name, parent_class_name) \
MMetaObject *class_name::staticMetaObject = \
new MMetaObject { #class_name, sizeof(class_name), \
parent_class_name::staticMetaObject };
//定義一個Animal類
class Animal : public MObject {
M_OBJECT_DECLARE(Animal)
};
M_OBJECT_IMPLEMENT(Animal, MObject)
//定義一個Cat類
class Cat : public Animal {
M_OBJECT_DECLARE(Cat)
};
M_OBJECT_IMPLEMENT(Cat, Animal)
可以看到,通過宏可以自動幫我們生成元對象,我們的代碼一下簡潔許多
而Qt的元對象系統,不止是用到了宏,還用到了Moc編譯器
由於Moc編譯器屬於編譯工具,它能做的自動化工作遠比宏要多