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()方法中就会报告一个编译错误。而且代码很容易使用:

 
  1. ObjectFactory::registerClass<Foo>();

  2. // ...

  3. QObject* foo = ObjectFactory::createObject( "Foo" );


      同时也很容易修改这个代码,来适用于那些不从QObject继承的自定义抽象类,例如它可以使用任何传递给registerClass()方法或者自动从类的静态成员接收的类型的“Key”,而不是使用从OMetaObject接收的类名作为“Key”.根据需要还有一组不同的参数可以传递给构造函数。

  • 1
    点赞
  • 14
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值