8

【大型软件开发】浅谈大型Qt软件开发(四)动态链接库的宏冲突问题、COM组件开发的常...

 1 year ago
source link: https://www.cnblogs.com/Leventure/p/17107739.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最近工作的时候有一个链接库的对接工作,在对接时发生了一些小问题,这篇FAQ是办公室写这个库的工程师戴工写的,这里记录一下:

一、编译工程时报链接错误“不允许dllimport静态数据成员的定义”

1.错误截图

image

2.错误原因分析

此错误是Q_OBJECT和Q_DECL_IMPORT宏共同作用时产生的结果。查询微软文档可知:静态数据成员无法在定义dllimport类的同一程序中指定定义。

image

这句话表明,导出类时,其静态成员不可被外部覆盖定义。举个例子,有导出类如下:

image

其中宏定义为:

image

根据微软关于dllimport的说明,类静态成员变量n不可被覆盖定义,即进行这样的操作

image

可以看到,对普通成员函数fun进行覆盖,仅仅发出了编译警告,但编译器并没有明确拒绝这种行为,但在对静态成员n进行覆盖定义时,编译器报错,拒绝了此行为。

回过头来看Q_OBJECT宏的定义:

image

发现静态成员staticMetaObject,根据QMAKE规则,QT编译时会对包含有Q_OBJECT宏的类进行一次moc工作,而moc_*.cpp中刚好拥有staticMetaObject的定义操作:

image

因此,Q_DECL_IMPORT(即dllimport)宏导出的类中包含了Q_OBJECT宏时,违反了编译器规则,因此无法完成编译。

3.修改策略

(1)修改导出类,如果导出类中含有Q_OBJECT宏,保留导出定义,去除导入定义;
(2)去除Q_DECL_IMPORT声明。由QT向导生成的宏声明文件中,有条件编译控制宏BUILD_STATIC,当工程中定义了该宏时,便可取消Q_DECL_IMPORT宏的导入声明;也可以不定义BUILD_STATIC宏,而是手动修改文件,去除Q_DECL_IMPORT宏。

image

(3)去除/注释Q_OBJECT宏。导入类时,可以在工程代码中将导入库头文件中的Q_OBJECT宏逐个注释。

image

(4)从工程中移除相关头文件。

image

二、创建dll导入类时报警告“QObject: Cannot create children for a parent that is in a different thread.”

1. 错误截图

2.错误原因分析

这可能是由于工程编译版本类型与链接库的类型不一致导致,比如工程当前为debug版本,但链接库编译生成版本为release

3.错误修改策略

将工程生成版本调整为与链接库版本一致

三、单例写法:

原先的单例写法比较简单,代码大概如下:

static Test&Test::Singleton(){
	static Test Instance;
	return Instance
}

这样的单例模式看上去还行,这个也是我之前常用的单例模式。但是这个在COM组件的开发中是不够安全的,原因也很简单~因为我们提交给COM组件的IDispatch是指针形式提交的,这个指针有可能会在外部被析构掉(只是提出一种可能,这里尚未测试和验证),所以这个单例模式是指针不安全的。

既然如此我们就需要修改一种比较好用且指针安全的单例模式。今天看了下小余的代码我感觉还挺好用的,拿过来抄一下:

static NetServer* NetServer::Singleton() {
	static QMutex mutext;
	static QSharedPointer<NetServer> inst;
	if (Q_UNLIKELY(!inst)) {
		if (!inst) {
			inst.reset(new NetServer());
		}
	}
	return inst.data();
}

这段代码就是用的QSharedPointer 智能指针对单例指针进行维护,这样只要主进程还在,这个单例的指针就会一直保存在QSharedPointer内,就不会被析构了,除非你自己提供了一个析构的方法。


Recommend

About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK