【零基礎學QT】【030】原生C++代碼模擬Qt類型識別系統

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編譯器屬於編譯工具,它能做的自動化工作遠比宏要多

發佈了429 篇原創文章 · 獲贊 43 · 訪問量 13萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章