从 C++ 到 Qt

转载自:http://hi.baidu.com/cyclone/blog/item/8f8f08fa52d22f8758ee9006.html

Qt 是 C++ 的库,Qt在ansi C++ 的基础上进行了一点扩展。

但国内似乎比较浮躁,学Qt的很多连基本的C++如何编译似乎都不太清楚。本文舍弃IDE或qmake、cmake等工具的束缚,尝试通过几个例子,一步一步从标准 C++ 的编译过渡到 Qt 的编译。

本文涉及的都是最基本的东西,或许可以说,只要你用C++ Qt,不管是通过哪种工具(qmake、cmake、boost.build、qtcreator、vs2008、Eclipse、...),本文的内容都是需要理解的(尽管真正写程序时,我们都不会直接用C++编译器来编译Qt程序)。

如果你对命令行比较恐惧,或许愿意先看看我原来整理的这个 http://wiki.ubuntu.org.cn/Gcchowto

例子一:简单的控制台程序

一个很简单的例子,没用到Qt扩展:(也就是说,这是一个普通的C++程序)

#include <QtCore/QCoreApplication>
#include <QtCore/QDebug>
int main(int argc, char** argv)
{
QCoreApplication app(argc, argv);
qDebug()<<"hello qt!";
app.exec();
}

我们都知道,编译一个C++的程序,无非是 编译预处理,编译、链接

  • 编译预处理器:头文件路径 和 必要的宏
  • 编译器:一些编译参数
  • 链接器:一些链接参数 和 要链接的库

g++

简单一行命令,即可生成 main.exe (linux下,则生成可执行程序 main)

g++ main.cpp -DQT_CORE_LIB -Ie:\Qt\4.7.0\include -o main -Le:\Qt\4.7.0\lib -lQtCore4

单行命令,很简单:

  • -I 指定头文件路径
  • -L 指定库文件路径
  • -l 指定需要链接的库
  • -D 定义必要的宏(其实对这个小程序,这个宏也没必要用)
  • -o 指定生成的可执行文件名

cl

简单一行命令,即可生成 main.exe

cl main.cpp -ID:/Qt/4.7.0/include -DQT_CORE_LIB -Femain  -link -LIBPATH:D:/Qt/4.7.0/lib QtCore4.lib

依然很简单

  • -I 头文件路径
  • -D 定义必要的宏
  • -Fe 指定可执行程序文件名
  • -link 后面是链接器参数
    • -LIBPATH 库文件路径

例子二:简单的GUI程序

这次稍微复杂一点,不是单一的控制台程序,而是一个简单的GUI程序

  • main.cpp
#include <QtGui/QApplication>
#include "widget.h" int main(int argc, char** argv)
{
QApplication app(argc, argv);
Widget w;
w.show();
return app.exec();
}
  • widget.h
#include <QtGui/QWidget>
class Widget : public QWidget
{
public:
Widget(QWidget * parent=NULL);
};
  • widget.cpp
#include "widget.h"

Widget::Widget(QWidget * parent)
:QWidget(parent)
{
}

同样,这个程序未使用Qt的扩展,直接用C++的编译器编译:

g++

g++ main.cpp widget.cpp -DQT_CORE_LIB -DQT_GUI_LIB -Ie:\Qt\4.7.0-beta2\include -o main  -Le:\Qt\4.7.0-beta2\lib -lQtCore4 -lQtGui4

因为我们使用了QtGui模块,所以和前面相比:

  • 增加了 -DQT_GUI_LIB 和 -lQtGui4
  • 多了一个文件 widget.cpp

注意: Windows下


如果在非windows平台下,这条命令就可以了。但windows下,你知道的:分console和windows两个链接子系统,而且入口函数分 main 和 WinMain 。

这条命令,编译出的 main.exe 会弹出控制台。要想不要控制台,则使用下面的命令:

g++ main.cpp widget.cpp -DQT_CORE_LIB -DQT_GUI_LIB -DQT_NEEDS_QMAIN -Ie:\Qt\4.7.0-beta2\include -o main  -Le:\Qt\4.7.0-beta2\lib -lQtCore4 -lQtGui4 -lqtmain -Wl,-subsystem,windows

多了两个选项:

  • qtmain 该库中一个WinMain 函数,它会调用我们的代码的main函数。即对编译器来说:入口函数的名字变了

  • -Wl,-subsystem,windows 你知道的,链接windows子系统
  • 对与MinGW来说,此处多了一个宏 QT_NEEDS_QMAIN,这个东西很有意思。在Qt Windows下链接子系统与入口函数(终结版) 中我们详细提到了这个。(在此处,不过你可以忽略它,不会出错,而且也不会有可感觉到的差异)

cl

同windows下的g++基本一样,带控制台:

cl main.cpp widget.cpp -ID:/Qt/4.7.0/include -DQT_CORE_LIB -DQT_GUI_LIB  -Femain  -link -LIBPATH:D:/Qt/4.7.0/lib QtCore4.lib QtGui4.lib

不带控制台:

cl main.cpp widget.cpp -ID:/Qt/4.7.0/include -DQT_CORE_LIB -DQT_GUI_LIB  -Femain /MD  -link -LIBPATH:D:/Qt/4.7.0/lib -subsystem:windows qtmain.lib  QtCore4.lib QtGui4.lib

分析同上:指定链接子系统,启用WinMain入口函数

多文件的程序如何管理

直接调用编译器有什么坏处呢?

  • 参数多啊,每次手动输入,难免出错。(例子中我们用的参数已经尽可能少了,可能都还是让你眼晕了)。
  • 其次呢,很重要的一点,每次只要一个文件修改,所有东西都要重新编译。

改变这种状况的办法,传统的就是写 Makefile,然后编译时只需要输入 make 就行了,他会判断哪些文件被改动需要重新编译。

另外就是VS等一些IDE自己提供的功能。下面简单看一下本例子对应makefile文件:

mingw32-make的Makefile文件

CPPFLAGS = -DQT_CORE_LIB -DQT_GUI_LIB -Ie:\Qt\4.7.0\include
LDFLAGS = -Le:\Qt\4.7.0\lib -lQtCore4 -lQtGui4 -lqtmain -Wl,-subsystem,windows objects = main.o widget.o
dest = main $(dest) : $(objects)
g++ -o $@ $(objects) $(LDFLAGS)

nmake的Makefile文件

CPPFLAGS = -ID:/Qt/4.7.0/include -DQT_CORE_LIB -DQT_GUI_LIB -MD
LDFLAGS = -LIBPATH:D:/Qt/4.7.0/lib -subsystem:windows qtmain.lib QtCore4.lib QtGui4.lib
objects = main.obj widget.obj
dest = main.exe
$(dest) : $(objects)
link $(objects) $(LDFLAGS)

对此不做介绍,因为Makefile编写也是一门学问。相当难写,所有才有qmake、cmake这些工具来帮我们生成Makefile文件

例子三:引入moc

Qt 对 C++ 的扩展主要是3个方面:

  • 元对象系统,包含Q_OBJECT宏的文件(.h, .cpp等)需要 moc 预处理
  • 资源系统,.qrc 文件 需要 rcc 进行预处理
  • 界面系统,.ui 文件 需要 uic 进行预处理

这3者之中,元对象系统最复杂,也是 Qt 程序中重要的。其他两个你都可以不要,唯独这个不要就有点不像话了(没它还叫Qt程序么?像我们前面写的,只不过是普通的C++程序)

废话少说,看例子:(修改前面的widget.h,加入Q_OBJECT)

#include <QtGui/QWidget>
class Widget : public QWidget
{
Q_OBJECT
public:
Widget(QWidget * parent=NULL);
};

如何编译这个程序呢?例子二中的命令还能用吗?不妨试试:

  • 哇,输出好丰富啊!

来自 g++ 的问候:

main.o:main.cpp:(.text$_ZN6WidgetD1Ev[Widget::~Widget()]+0xb): undefined reference to `vtable for Widget'
main.o:main.cpp:(.text$_ZN6WidgetD1Ev[Widget::~Widget()]+0x15): undefined reference to `vtable for Widget'
widget.o:widget.cpp:(.text+0x39): undefined reference to `vtable for Widget'
widget.o:widget.cpp:(.text+0x43): undefined reference to `vtable for Widget'

来自 cl 的问候:

widget.obj : error LNK2001: 无法解析的外部符号 "public: virtual struct QMetaObject const * __thiscall Widget::metaObject(void)const " (?metaObject@Widget@@UBEPBUQMetaObject@@XZ)
widget.obj : error LNK2001: 无法解析的外部符号 "public: virtual void * __thiscall Widget::qt_metacast(char const *)" (?qt_metacast@Widget@@UAEPAXPBD@Z)
widget.obj : error LNK2001: 无法解析的外部符号 "public: virtual int __thiscall Widget::qt_metacall(enum QMetaObject::Call,int,void * *)" (?qt_metacall@Widget@@UAEHW4Call@QMetaObject@@HPAPAX@Z)

发生了什么?

添加一个宏后,发生了什么?我们看看编译器将宏展开后是什么样子的:

#include <QtGui/QWidget>
class Widget : public QWidget
{
static const QMetaObject staticMetaObject;
virtual const QMetaObject *metaObject() const;
virtual void *qt_metacast(const char *);
virtual int qt_metacall(QMetaObject::Call, int, void **);
...
public:
Widget(QWidget * parent=NULL);
};

一下子多出来这么多函数,而且还没有函数体,不出错才怪。如何生成函数体呢?这正是moc所做的:

moc widget.h -o moc_widget.cpp

这样一来,这些函数都在 moc_widget.cpp 被实现了,只要我们将该文件一块编译链接就行了

对g++来说,在例子二的基础上,直接添加一个 moc_widget.cpp 文件,然后一切正常了:

g++ main.cpp widget.cpp moc_widget.cpp -DQT_CORE_LIB -DQT_GUI_LIB -Ie:\Qt\4.7.0-beta2\include -o main  -Le:\Qt\4.7.0-beta2\lib -lQtCore4 -lQtGui4

对 cl 编译器,同样只要添加一个 moc_widget.cpp 即可。

例子四,rcc和uic

有点糟蹋这个名字了,本节中不讲例子(因为 rcc 和 uic 概念比较简单)

  • 如果我们用了资源,那么需要一个 xxx.qrc 文件,这个文件呢,C++ 编译器不认识,于是
rcc xxx.qrc -o qrc_xxx.cpp
  • 如果我们用了designer设计的界面 .ui。C++ 编译器不认识这个文件,于是
uic xxx.ui -o ui_xxx.h

这样一来,我们得到是就全是 .h 和 .cpp 的文件了,剩下的工作,你知道的,交给 C++ 编译器就行了。

其他

现在来看这个图:是不是很简单了?

http://blog.csdn.net/wsh6759/article/details/7432247

从 C++ 到 Qt(命令行编译)good的更多相关文章

  1. Windows下使用命令行编译Qt项目(解决DLL丢失问题)

    一.前言 我之前用Qt做了个hello world,结果各种报错,一大堆DLL找不到,今天用命令行编译就通过了 二.准备工作 1.Visual Studio(有nmake就行) 2.Qt 3.把qma ...

  2. 使用命令行编译Qt程序

    code[class*="language-"], pre[class*="language-"] { color: rgba(51, 51, 51, 1); ...

  3. 基于命令行编译打包phonegap for android应用 分类: Android Phonegap 2015-05-10 10:33 73人阅读 评论(0) 收藏

    也许你习惯了使用Eclipse编译和打包Android应用.不过,对于使用html5+js开发的phonegap应用,本文建议你抛弃Eclipse,改为使用命令行模式,绝对的快速和方便. 一直以来,E ...

  4. 用命令行编译java并生成可执行的jar包

    用命令行编译java并生成可执行的jar包 1.编写源代码. 编写源文件:CardLayoutDemo.java并保存,例如:I:\myApp\CardLayoutDemo.java.程序结构如下: ...

  5. iOS系统提供开发环境下命令行编译工具:xcodebuild

    iOS系统提供开发环境下命令行编译工具:xcodebuild[3] xcodebuild 在介绍xcodebuild之前,需要先弄清楚一些在XCode环境下的一些概念[4]: Workspace:简单 ...

  6. namke 命令行编译

    简介 大家已经习惯于微软提供的功能强大的IDE,已经很少考虑手动编连项目了,所谓技多不压身,有空的时候还是随我一块了解一下命令行编译. C/C++/VC++程序员或有Unix/Linux编程经验应该很 ...

  7. xcode命令行编译时:codesign命令,抛出“User interaction is not allowed.”异常 的处理

    之前正常运行的hudson iOS编译服务器slave节点,忽然出现编译失败.发现原因有2个: 第一个原因是编译机上用来签名的用户帐号过期,第二个原因是操作系统和xCode升级造成的. 对于第一个,重 ...

  8. 命令行编译运行Java

    首先要安装JDK,然后设置环境变量Path,添加C:\Program Files (x86)\Java\jdk1.8.0_66\bin 然后建立一个名为j.java的文件,里面加入如下代码: publ ...

  9. VS2010命令行编译C#和VC项目

    VS2010命令行编译C#和VC项目 VS2010命令行编译C#和VC项目 根据需要动态创建数据库字段后,需要动态创建或者调整页面,那就需要编译这些页面和后台文件.因此使用命令行编译将会非常方便,对于 ...

  10. Android 命令行编译、打包生成apk文件

    一.搭建搭建环境 1. 安装JDK 和 Android SDK   2. 配置环境变量 D:\android-sdk-windows\tools C:\Program Files\Java\jdk1. ...

随机推荐

  1. Android studio 删除Module、project

    很简单: 1 选中项目右键:Open Module Setting 2 选择要删除的项目,点击“-”即可

  2. 利用openssl进行RSA加密解密

    openssl是一个功能强大的工具包,它集成了众多密码算法及实用工具.我们即可以利用它提供的命令台工具生成密钥.证书来加密解密文件,也可以在利用其提供的API接口在代码中对传输信息进行加密. RSA是 ...

  3. 敌情篇 ——DDoS攻击原理

    敌情篇 ——DDoS攻击原理 DDoS攻击基础 DDoS(Distributed Denial of Service,分布式拒绝服务)攻击的主要目的是让指定目标无法提供正常服务,甚至从互联网上消失,是 ...

  4. Jquery 1.11.1 Checkbox checked 判断

    if($(this).prop('checked'))

  5. serialize

    Jquery的serialize()方法用于将表单元素转换为查询字符串格式, Submit按钮及File选择元素是不能序列化的. $('input:button').click(function() ...

  6. 开启或关闭SQLSERVER服务的bat文件

    界面如下: 因为电脑的SQLSERVER服务没有自己启动,有时候又需要关闭SQLSERVER服务,就自己在网上找了下教程. 源码如下: @echo offchoice /t 3 /c yn /d y ...

  7. <<深入Java虚拟机>>-第三章-垃圾收集器与内存分配策略-学习笔记

    垃圾收集 垃圾收集(Garbage Collection,GC),垃圾收集需要完成的三件事情. 哪些对象需要回收 什么时候回收 如何回收 如何确定对象已死(即不可能在被任何途径引用的对象) 引用计数算 ...

  8. asp.net <% %>,<%# %>,<%= %>,<%$ %>区别大集合

    前台页面 <div><%--可以执行服务器代码,相当于在后台写代码,Render%><%=取后台变量或方法值,只能绑定客户端控件,绑定服务器控件时后来必须调用databi ...

  9. PHP — 用PHP实现一个双向队列

    1.简介 deque,全名double-ended queue,是一种具有队列和栈的性质的数据结构.双端队列中的元素可以从两端弹出,其限定插入和删除操作在表的两端进行.双向队列(双端队列)就像是一个队 ...

  10. Challenge Checkio(python)—初尝python练习网站

    最近在找点python语言练习的网站,发现这个网站不错 http://www.checkio.org/ 页面设计的也比较漂亮,比较适合学习python的语法知识.不过注册这个网站 开始就得解决一个py ...