QT 动态创建对象

原文:http://www.mimec.org/node/350

在我继续一系列的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的类的创建工厂:

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 );
    }

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;
    }
}; 

 

       使用这种途径,不在需要使用Q_INVOKABLE声明构造器了,而且如果没有找到合适的构造器,只要这个类注册了,在constructorHelper()方法中就会报告一个编译错误。而且代码很容易使用:

ObjectFactory::registerClass<Foo>();

// ...

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


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

 

  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值