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 动态创建对象(第一种方法)相关推荐
- 利用Qt元对象技术防止工厂模式下代码臃肿问题,QT 动态创建对象(第2种方法)
问题的提出: 近来要编写一个仿真液压.电力.机械的软件,如下为液压的: 可以看到液压图中很多液压元器件,这些元器件的id.名称等都是从json配置文件读取的,配置文件格式如下: {"Clas ...
- Qt5.9一个简单的多线程实例(类QThread)(第一种方法)
Qt开启多线程,主要用到类QThread.有两种方法,第一种用一个类继承QThread,然后重新改写虚函数run().当要开启新线程时,只需要实例该类,然后调用函数start(),就可以开启一条多线程 ...
- Qt绑定UI界面和Qt类的四种方法
1. Qt类头文件中 声明命名空间 namespace Ui { class Widget; } 声明UI指针对象 public:explicit Widget(QWidget *parent = 0 ...
- 【错误记录】NDK 导入外部 so 动态库报错 ( java.lang.UnsatisfiedLinkError | Android Studio 配置外部 so 动态库两种方法 )
文章目录 一.报错信息 二.解决方案 ( Android Studio 配置外部 so 动态库两种方法 ) 1.jniLibs 目录存放 2.libs 目录存放 一.报错信息 外部引用 so 动态库 ...
- Javascript创建对象几种方法解析
Javascript创建对象几种方法解析 Javascript面向对象编程一直是面试中的重点,将自己的理解整理如下,主要参考<Javascript高级程序设计 第三版>,欢迎批评指正. 通 ...
- 【C语言】求一千以内的素数 第一种方法
第一种方法:平常思维(人们第一印象会想到的) //什么是素数? -- 除了1和本身之外不能被其他数整除的数 #include "stdio.h"int main(){int n,j ...
- JAVASE基础模块三十五( 线程 线程创建的第一种方法 以及线程的一些方法)
JAVASE基础模块三十五( 线程 线程创建的第一种方法 以及线程的一些方法) 线程 首先要清楚的是 线程依赖于进程 进程 是 正在运行的应用程序 一个正在运行的应用程序 是个进程 这个应用程序又要执 ...
- 反射学习笔记之动态创建对象和调用方法
动态加载和静态引用的程序集并不是同一个Assembly了.事实上,在.Net中,同一个应用程序域并不允许同时加载两个相同的Assembly.即使加载了,也会认为是两个不同的程序集.如果要同时加载两个, ...
- C/C++|Qt工作笔记-4种方法判断当前对象(类)名或标识(继承发,typeid法,元对象className()法,Q_CLASSINFO法)
回想起3个月前,刚刚参加工作也做过类似的笔记,但只有2种方法,估计刚毕业没有什么墨水,经过3个月时间又多了2种方法: 这些方法都可用于RTTI 第一个方法是继承发(C++中很推荐用这个,感觉用这个结构 ...
- u盘启动计算机的几种方式,进入U盘启动模式的启动模式是什么?第一种方法是输入BIO...
说到该模式,每个人都应该知道,有些朋友在引导时问如何启动U盘安装系统,有些人想问如何进入U盘来启动计算机. 到底是怎么回事?其实如何设置启动U盘启动?下面的编辑器组织了按一下启动键即可进入U盘启动模式 ...
最新文章
- APUE读书笔记-14高级输入输出-06异步IO
- python什么是调用_python open需要调用什么
- 扩展--使用队列来优化递归操作完成文件下载
- [问题解决]基于注解配置dubbo遇到ConnectionLoss for /dubbo/xxx问题解决
- 定位低效SQL与不同的Extra类型(转载)
- Bugku杂项-convert
- JavaScript 继承
- How to install VNC on Ubuntu
- Atitit. 解决80端口 System 占用pid 4,,找到拉个程序或者服务占用http 80服务
- 飞思卡尔单片机高效c语言编程,飞思卡尔单片机高效c语言编程(中文)新.pdf
- jmeter连接mysql并定义变量提供给后续接口使用
- HBase 从下载到安装和运行
- 一套优秀的直播系统源码是什么样的?起码要有这五个模块
- ABBYY软件PDF文本审阅操作之批阅文本
- 为什么篮球一进游戏就服务器中断,街头篮球手游进不去 进不去游戏无非这两种原因...
- 微信提现显示服务器异常,微信零钱提现为什么显示提示交易异常 解决办法是什么...
- `算法竞赛题解` LCP 03. 机器人大冒险
- 微信公众号留言功能实现方法分享
- 老王的电影网站 - 推荐系统入门(一)
- 怎么用手机压缩图片?教给大家三种手机压缩图片方法
热门文章
- andriod studio怎么设置图片大小_Word图片大小总是对不齐,如何统一图片的大小位置,看一眼就会!...
- typescript索引类型_TypeScript的索引类型与映射类型,以及常用工具泛型的实现
- modbus报文解析实例_云原生、全栈可编程的下一代SDN解析与实践 (一)丨传统SDN架构演进...
- git 修改分支名字_大牛总结的 Git 使用技巧,写得太好了!
- windows下多tomcat部署
- IPv6应用普及,任重而道远
- IDEA @Override is not allowed when implementing interface method(转载)
- 【腾讯Bugly干货分享】那些年,我们一起写过的“单例模式”
- 魅力 .NET:从 Mono、.NET Core 说起
- BroadcastReceive之ip拨号