QT 动态创建对象(第一种方法)
在我繼續(xù)一系列的Qt數(shù)據(jù)序列化文章之前,有一個(gè)相對(duì)重要的需要提及的話題,那就是:基于類名動(dòng)態(tài)創(chuàng)建類對(duì)象的能力。
? ? ? ? 假定現(xiàn)在我們要?jiǎng)?chuàng)建一系列的形狀,形狀是一個(gè)抽象類,實(shí)際類是存儲(chǔ)在一個(gè)列表中的各種各樣的派生類:矩形、圓等等。在序列化期間,我們可以保存每一項(xiàng)的類名和對(duì)象數(shù)據(jù),在反序列化(即加載數(shù)據(jù))時(shí),我們需要能夠創(chuàng)建合適類實(shí)例的能力,這就是要用到一個(gè)對(duì)象工廠的地方。在支持反射的語言中,例如C#、Java,僅需要幾行代碼就可以從一個(gè)跟定的類名字符串獲得一個(gè)類實(shí)例。但是在c++中沒有這樣的機(jī)制。
? ? ? ?一個(gè)簡單的解決方案是創(chuàng)建一個(gè)單獨(dú)的函數(shù),里面有一個(gè)大的switch塊(或者一系列的switch塊)來創(chuàng)建合適的類對(duì)象,盡管這種方法不雅觀并且破壞了面向?qū)ο笤O(shè)計(jì),但是大多數(shù)情況下是可以接受的。然而,當(dāng)你有很多的類而且分散在應(yīng)用程序的不同模塊時(shí),使用上述方法可能會(huì)變得難以管理,而且當(dāng)應(yīng)用程序有擴(kuò)展的模塊和動(dòng)態(tài)加載的插件時(shí),這就會(huì)變得更加困難。
? ? ? 另一個(gè)更優(yōu)雅的解決方法是有一個(gè)不需要知道任何對(duì)象類型的工廠,而要通過工廠實(shí)例化的類必須使用某種內(nèi)部圖來先注冊,這樣,每個(gè)模塊或插件可以獨(dú)立的注冊它們各自類。
? ? ? ?QT有兩種可以用來創(chuàng)建這樣的工廠的機(jī)制,它們看起來相似,實(shí)際上有很大的差別:
? ? ? ?QMetaType
? ? ? ?construct()方法能夠用來創(chuàng)建任何內(nèi)建類型的實(shí)例,或者是通過Q_DECLARE_METATYPE宏指定的自定義類型。這是QVariant所要做的,用來內(nèi)部封裝自定義類型。然而這種機(jī)制是用來供變量類型使用的,也就是有默認(rèn)構(gòu)造和拷貝函數(shù)的類,但是對(duì)于抽象類對(duì)象是沒有意義的,因?yàn)槌橄箢愅ǔJ褂弥羔槀鬟f,并且拷貝構(gòu)造通常被禁用。
? ? ? ?QMetaObject
? ? ? ?newInstance()方法可以用來創(chuàng)建任何一個(gè)從QObject派生下來的類的實(shí)例,僅有的條件是類的構(gòu)造器必須通過Q_INVOKABLE修飾,來明確地聲明。這和多態(tài)對(duì)象配合可以很好的工作,因?yàn)镼Object類通常作為各種抽象類的基類。值得注意的是,從QT4開始,若沒有額外的工作,僅僅依靠類名是不可能檢索到QMetaObject的。
? ? ? ?可以很容易的創(chuàng)建一個(gè)依賴于QMetaObject的對(duì)象工廠,這里有一種實(shí)現(xiàn),不過這種解決方法也有一些缺點(diǎn):
? ? ? ?構(gòu)造器必須使用Q_InVOKABLE顯示聲明,以便能夠訪問QMetaObject;
? ? ? ?沒有在編譯期檢查是否存在合適的構(gòu)造函數(shù)可以訪問,或者參數(shù)類型是否正確,當(dāng)你實(shí)際嘗試創(chuàng)建實(shí)例時(shí),僅僅會(huì)得到一個(gè)運(yùn)行時(shí)警告,并返回空指針;
? ? ? 子類化QObject會(huì)增加每個(gè)對(duì)象實(shí)例的內(nèi)存占用,當(dāng)執(zhí)行運(yùn)行時(shí)類型檢查時(shí),通過QMetaObject進(jìn)行的動(dòng)態(tài)方法調(diào)用也會(huì)存在一些開銷。
? ? ? 然而,創(chuàng)建一個(gè)可以創(chuàng)建任何類的自定義類工廠也不是難事,下面是一個(gè)適用于任何繼承于QObject的類的創(chuàng)建工廠,如下為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(){// 最后一個(gè)參數(shù)是函數(shù)指針,只有才調(diào)用時(shí)才需要傳入?yún)?shù)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其實(shí)是 registerClass()函數(shù)中傳入的函數(shù)指針}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聲明構(gòu)造器了,而且如果沒有找到合適的構(gòu)造器,只要這個(gè)類注冊了,在constructorHelper()方法中就會(huì)報(bào)告一個(gè)編譯錯(cuò)誤。而且代碼很容易使用:
ObjectFactory::registerClass<Foo>();
// ...
QObject* foo = ObjectFactory::createObject( "Foo" );
? ? ? 同時(shí)也很容易修改這個(gè)代碼,來適用于那些不從QObject繼承的自定義抽象類,例如它可以使用任何傳遞給registerClass()方法或者自動(dòng)從類的靜態(tài)成員接收的類型的“Key”,而不是使用從OMetaObject接收的類名作為“Key”.根據(jù)需要還有一組不同的參數(shù)可以傳遞給構(gòu)造函數(shù)。
總結(jié)
以上是生活随笔為你收集整理的QT 动态创建对象(第一种方法)的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 蓝牙耳机可以打电话吗(蓝牙无线技术)
- 下一篇: h文件和c文件的关系是什么