前段时间说了Qt一些类库的使用,今天来换一下口味,来看一下程序设计的问题。今天来说的是关于共享库 shared library。

如果你打开一些 Windows 应用程序的目录,你会发现有很多程序的 exe 文件都很小,大约几百K 的样子,并且目录中不仅仅只有一个 exe 文件,还包含着一大堆 dll 文件。这些 dll 其实就是一些共享库,所谓共享库,其实就是一些动态链接库,能够由程序在运行时进行动态加载的库。既然说是共享,那就是说,这些库不仅仅自己的程序可以使用,并且其他程序也可以使用,例如某些通用算法。如果你发布一下自己编写的 Qt 程序,也会看到很多系统的共享库,就是那些 QtGui.dll 之类的东西。或许你会说,我写的程序没有同其他应用共享的库,就不需要这些了吧!其实不然。因为共享库的一个好处是可以动态加载,也就是说,如果你需要升级程序,那么就要简单的替换掉这个 dll 就好了,不需要要求用户重新安装全部文件。当然,这些 dll 也是有缺点的:动态加载的东西肯定会比静态编译的东西效率低一些。不过在现在的硬件环境下,这点性能损失已经可以忽略不计了。

今天我们要说的就是如何用 Qt 创建共享库代码。

我们还是使用 QtCreator。在创建工程的时候,我们选择下面的 C++ Library 一项,然后点击 OK。

在接下来的对话框中,有一个下拉列表,分别是 Shared Library(共享库),Statically Linked Library(静态链接库)和 Qt 4 Plugin(Qt 4 插件)。我们选择第一个共享库,后面的步骤中会要求选择加入哪几个 Qt 模块,和前面一样,选择自己需要的部分,最后完成工程的创建。

我们会看到 QtCreator 已经帮我们创建好了一些文件。其中有一个 {projectName}_global.h 的文件是 QtCreator 替我们创建的。下面我们就从这个 {projectName}_global.h 开始:

  1. #ifndef LIB_GLOBAL_H
  2. #define LIB_GLOBAL_H
  3. #include <QtCore/qglobal.h>
  4. #if defined(LIB_LIBRARY)
  5. #  define LIBSHARED_EXPORT Q_DECL_EXPORT
  6. #else
  7. #  define LIBSHARED_EXPORT Q_DECL_IMPORT
  8. #endif
  9. #endif // LIB_GLOBAL_H

这个文件中只是定义了两个宏 LIBSHARED_EXPORT,注意这里的 LIB 就是我的工程名字。如果定义了 LIB_LIBRARY,LIBSHARED_EXPORT 定义为 Q_DECL_EXPORT,否则定义为 Q_DECL_IMPORT。看这个名字,就知道这就是把对象导出的语句了。下面我们来编写一个窗口(如果你希望这么做,不要忘记在创建工程时勾选 QtGui 模块,默认是不勾选的):

lib.h

  1. #ifndef LIB_H
  2. #define LIB_H
  3. #include <QMainWindow>
  4. #include "lib_global.h"
  5. class LIBSHARED_EXPORT MainWindow : public QMainWindow {
  6. public:
  7. MainWindow(QWidget *parent = 0);
  8. };
  9. #endif // LIB_H

lib.cpp

  1. #include "lib.h"
  2. MainWindow::MainWindow(QWidget *parent)
  3. : QMainWindow(parent)
  4. {
  5. }

代码很简单,就是创建一个 MainWindow。同前面的代码唯一不同的是,在头文件中,使用了 LIBSHARED_EXPORT 这个宏。你可以简单的把它理解成,我需要把这个类 MainWindow 导出。所谓导出,就是将其编译成一个 dll 文件之后,其他的类可以使用这个导出类。好了,下面和原来一样,编译一下这个工程。在 debug 文件夹下你得到的是一个 lib.dll 文件和 liblib.a。后者是 Linux 下使用的库,这里不再详述。

好了,我们要去使用这个 dll 了。新建另外一个工程,需要吧 .pro 文件修改一下:

  1. TARGET = test
  2. TEMPLATE = app
  3. SOURCES += main.cpp
  4. INCLUDEPATH += ../
  5. LIBS += ../debug/lib.dll

首先,我们添加了 INCLUDEPATH 这一行。这一行就是为了让我们的 test 项目可以找到 lib.h 和 lib_global.h 这两个文件,你需要把这里的路径替换成符合你的工程的路径。LIBS 这一行则需要告诉编译器(注意,这里是编译器!)到哪里去找到这个 dll 文件。然后我们编写 main.cpp:

  1. #include <QtGui/QApplication>
  2. #include "lib.h"
  3. int main(int argc, char *argv[])
  4. {
  5. QApplication a(argc, argv);
  6. MainWindow w;
  7. w.show();
  8. return a.exec();
  9. }

注意,我们使用了 lib.h,但是这个文件并没有在 HEADERS 里面声明,Qt 实际上就是从 INCLUDEPATH 这里去找到这个文件。MainWindow 在新建的 test 工程中并没有声明,那么它在哪里呢?当然就是在我们编译出来的 lib.dll 里面啦!我们知道,在链接的时候编译器需要找到实现入口,也就是必须定位到这个 dll,这就是由这个 LIBS 指定的地方。

最后编译运行一下这个 exe 文件,怎么样?哦,如果你照我说的做了的话,你应该得到一个错误:找不到 lib.dll。怎么会找不到呢?不是使用 LIBS 指定了吗?请注意,我们强调了,这个指定是编译期的。dll 是动态链接库,也就是说,在 exe 运行的时候需要找到这个库。运行时查找的顺序是:当前路径 -> 系统路径(通常是 system32)。所以,要把我们先前生成的这个 lib.dll 复制到 exe 所在目录,然后直接双击一下这个 exe 文件。一个窗口出来了!有什么区别吗?运行起来是没有区别的,但是我们知道,这个窗口是在这个 dll 里面实现的!我们想往窗口里面加个按钮?没问题,那就加吧!加完之后重新编译一个新的 dll,复制到 exe 文件夹覆盖旧的,修改就完成啦!我们不需要修改这个 exe 了。

这个时候我们再来回忆一下,我们使用自己创建的 dll 的时候,是不是就和使用 QtGui.dll 一样呢?只不过QtGui.dll 已经放在了库目录下, 不需要手动修改 .pro 文件添加 INCLUDEPATH 和 LIBS 罢了。

本文出自 “豆子空间” 博客,请务必保留此出处http://devbean.blog.51cto.com/448512/319479

Qt学习之路(60): 创建shared library的更多相关文章

  1. Qt 学习之路 2(60):使用 DOM 处理 XML

    Qt 学习之路 2(60):使用 DOM 处理 XML  豆子  2013年8月3日  Qt 学习之路 2  9条评论 DOM 是由 W3C 提出的一种处理 XML 文档的标准接口.Qt 实现了 DO ...

  2. QT学习之路--创建一个对话框

    Q_OBJECT:这是一个宏,凡是定义信号槽的类都必须声明这个宏. 函数tr()全名是QObject::tr(),被他处理过的字符串可以使用工具提取出来翻译成其他语言,也就是做国际化使用. 对于QT学 ...

  3. Qt 学习之路 2(70):进程间通信

    Qt 学习之路 2(70):进程间通信 豆子 2013年11月12日 Qt 学习之路 2 16条评论 上一章我们了解了有关进程的基本知识.我们将进程理解为相互独立的正在运行的程序.由于二者是相互独立的 ...

  4. Qt 学习之路 2(53):自定义拖放数据

    Qt 学习之路 2(53):自定义拖放数据 豆子  2013年5月26日  Qt 学习之路 2  13条评论上一章中,我们的例子使用系统提供的拖放对象QMimeData进行拖放数据的存储.比如使用QM ...

  5. Qt 学习之路 2(51):布尔表达式树模型

    Qt 学习之路 2(51):布尔表达式树模型 豆子 2013年5月15日 Qt 学习之路 2 17条评论 本章将会是自定义模型的最后一部分.原本打算结束这部分内容,不过实在不忍心放弃这个示例.来自于 ...

  6. Qt 学习之路 2(38):存储容器

    Qt 学习之路 2(38):存储容器 豆子 2013年1月14日 Qt 学习之路 2 38条评论 存储容器(containers)有时候也被称为集合(collections),是能够在内存中存储其它特 ...

  7. Qt 学习之路 2(27):渐变

    Qt 学习之路 2(27):渐变 豆子 2012年11月20日 Qt 学习之路 2 17条评论 渐变是绘图中很常见的一种功能,简单来说就是可以把几种颜色混合在一起,让它们能够自然地过渡,而不是一下子变 ...

  8. Qt 学习之路 2(18):事件

    Home / Qt 学习之路 2 / Qt 学习之路 2(18):事件 Qt 学习之路 2(18):事件  豆子  2012年9月27日  Qt 学习之路 2  60条评论 事件(event)是由系统 ...

  9. Qt 学习之路 2(5):自定义信号槽

    Home / Qt 学习之路 2 / Qt 学习之路 2(5):自定义信号槽 Qt 学习之路 2(5):自定义信号槽  豆子  2012年8月24日  Qt 学习之路 2  131条评论 上一节我们详 ...

随机推荐

  1. Java面向对象的编程

    类的多态性: Java语言中含有方法重载与成员覆盖两种形式的多态:(区别于c++) 方法重载:在一个类中,允许多个方法使用同一个名字,但方法的参数不同,完成的功能也不同. 成员覆盖:子类与父类允许具有 ...

  2. iOS获取运营商信号强度

    此API是apple私有API,所以只可运用在越狱设备中,如果提交appstore,会遭遇apple的拒绝上架反馈! #import <dlfcn.h> int getSignalLeve ...

  3. js中的setTimeout和setInterval

    在html页面中要使用自动刷新功能时,可以是使用js中setTimeout和setInterval: 一.使用方法 setTimeout的使用setTimeout('要调用的Js方法', 调用的延迟时 ...

  4. 个人自建网店(WordPress WooCommerce on SAE)集成支付宝支付

    插件: Alipay For WooCommerce 到支付宝账户的商家服务提出申请: https://b.alipay.com/order/serviceIndex.htm 在"在线签约- ...

  5. HTML5 总结-音频-2

    HTML5 音频 音频格式 当前,audio 元素支持三种音频格式:   IE 9 Firefox 3.5 Opera 10.5 Chrome 3.0 Safari 3.0 Ogg Vorbis   ...

  6. Nginx中location配置[转]

    关于一些对location认识的误区 1. location 的匹配顺序是“先匹配正则,再匹配普通”. 矫正: location 的匹配顺序其实是“先匹配普通,再匹配正则”.我这么说,大家一定会反驳我 ...

  7. {% load staticfiles %}

    原先写法 {# <link rel="stylesheet" href="/static/css/reset.css"/>#} 不使用路径写法,方便 ...

  8. 在QuartusII 中使用tcl对工程进行复制——半自动

    最近在看coyoo的博客,加上手上有一本coyoo老师的书籍.本人是脚本小白,怎么看都没有看懂这个自动化是怎么实现的. 先说我的半自动化,后面在说我对自动化的一点疑惑—— 目前没有实现 目录环境: 首 ...

  9. use utf8

    [root@wx03 0724]# cat a2.pl use Encode; my $a=<STDIN>; my $b=encode_utf8('微信'); print "\$ ...

  10. 安装ubuntu时的注意事项----个人小总结

    今天重装了一次ubuntu,以前是别人帮我装的,而这次是我自己照着网上教程装的. 这个教程还是挺不错的,我就是照着这个装成功的 http://jingyan.baidu.com/article/60c ...