QT 动态创建对象(第一种方法)
在我繼續一系列的Qt數據序列化文章之前,有一個相對重要的需要提及的話題,那就是:基于類名動態創建類對象的能力。
? ? ? ? 假定現在我們要創建一系列的形狀,形狀是一個抽象類,實際類是存儲在一個列表中的各種各樣的派生類:矩形、圓等等。在序列化期間,我們可以保存每一項的類名和對象數據,在反序列化(即加載數據)時,我們需要能夠創建合適類實例的能力,這就是要用到一個對象工廠的地方。在支持反射的語言中,例如C#、Java,僅需要幾行代碼就可以從一個跟定的類名字符串獲得一個類實例。但是在c++中沒有這樣的機制。
? ? ? ?一個簡單的解決方案是創建一個單獨的函數,里面有一個大的switch塊(或者一系列的switch塊)來創建合適的類對象,盡管這種方法不雅觀并且破壞了面向對象設計,但是大多數情況下是可以接受的。然而,當你有很多的類而且分散在應用程序的不同模塊時,使用上述方法可能會變得難以管理,而且當應用程序有擴展的模塊和動態加載的插件時,這就會變得更加困難。
? ? ? 另一個更優雅的解決方法是有一個不需要知道任何對象類型的工廠,而要通過工廠實例化的類必須使用某種內部圖來先注冊,這樣,每個模塊或插件可以獨立的注冊它們各自類。
? ? ? ?QT有兩種可以用來創建這樣的工廠的機制,它們看起來相似,實際上有很大的差別:
? ? ? ?QMetaType
? ? ? ?construct()方法能夠用來創建任何內建類型的實例,或者是通過Q_DECLARE_METATYPE宏指定的自定義類型。這是QVariant所要做的,用來內部封裝自定義類型。然而這種機制是用來供變量類型使用的,也就是有默認構造和拷貝函數的類,但是對于抽象類對象是沒有意義的,因為抽象類通常使用指針傳遞,并且拷貝構造通常被禁用。
? ? ? ?QMetaObject
? ? ? ?newInstance()方法可以用來創建任何一個從QObject派生下來的類的實例,僅有的條件是類的構造器必須通過Q_INVOKABLE修飾,來明確地聲明。這和多態對象配合可以很好的工作,因為QObject類通常作為各種抽象類的基類。值得注意的是,從QT4開始,若沒有額外的工作,僅僅依靠類名是不可能檢索到QMetaObject的。
? ? ? ?可以很容易的創建一個依賴于QMetaObject的對象工廠,這里有一種實現,不過這種解決方法也有一些缺點:
? ? ? ?構造器必須使用Q_InVOKABLE顯示聲明,以便能夠訪問QMetaObject;
? ? ? ?沒有在編譯期檢查是否存在合適的構造函數可以訪問,或者參數類型是否正確,當你實際嘗試創建實例時,僅僅會得到一個運行時警告,并返回空指針;
? ? ? 子類化QObject會增加每個對象實例的內存占用,當執行運行時類型檢查時,通過QMetaObject進行的動態方法調用也會存在一些開銷。
? ? ? 然而,創建一個可以創建任何類的自定義類工廠也不是難事,下面是一個適用于任何繼承于QObject的類的創建工廠,如下為Foo.h類:
class Foo :public QObject {Q_OBJECT public:Foo(QObject*) {}; }; #include"Foo.h" #include<QHash>class ObjectFactory { public:template<typename T>static void registerClass(){// 最后一個參數是函數指針,只有才調用時才需要傳入參數constructors().insert(T::staticMetaObject.className(), &constructorHelper<T>);}static QObject* createObject(const QByteArray& className, QObject* parent = NULL){Constructor constructor = constructors().value(className);if (constructor == NULL)return NULL;return (*constructor)(parent); // constructor其實是 registerClass()函數中傳入的函數指針}private:typedef QObject* (*Constructor)(QObject* parent);template<typename T>static QObject* constructorHelper(QObject* parent ){return new T(parent);}static QHash<QByteArray, Constructor>& constructors(){static QHash<QByteArray, Constructor> instance;return instance;} };int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);ObjectFactory::registerClass<Foo>();QObject* foo = ObjectFactory::createObject("Foo");return a.exec(); }使用這種途徑,不在需要使用Q_INVOKABLE聲明構造器了,而且如果沒有找到合適的構造器,只要這個類注冊了,在constructorHelper()方法中就會報告一個編譯錯誤。而且代碼很容易使用:
ObjectFactory::registerClass<Foo>();
// ...
QObject* foo = ObjectFactory::createObject( "Foo" );
? ? ? 同時也很容易修改這個代碼,來適用于那些不從QObject繼承的自定義抽象類,例如它可以使用任何傳遞給registerClass()方法或者自動從類的靜態成員接收的類型的“Key”,而不是使用從OMetaObject接收的類名作為“Key”.根據需要還有一組不同的參數可以傳遞給構造函數。
總結
以上是生活随笔為你收集整理的QT 动态创建对象(第一种方法)的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝牙耳机可以打电话吗(蓝牙无线技术)
- 下一篇: s3c2440移植MQTT