1. 程式人生 > >從C++到Qt

從C++到Qt

原文釋出在百度空間,但由於百度空間屢次改版,越改越差,以至於有關閉可能,故而轉移到此 http://blog.debao.me

Qt 是 C++ 的庫,Qt 在 ansi C++ 的基礎上進行了一點擴充套件。

但國內似乎比較浮躁,學Qt的很多連基本的C++如何編譯似乎都不太清楚。本文捨棄IDE或qmake、cmake等工具的束縛,嘗試通過幾個例子,一步一步從標準 C++ 的編譯過渡到 Qt 的編譯。

本文涉及的都是最基本的東西,或許可以說,只要你用C++ Qt,不管是通過哪種工具(qmake、cmake、boost.build、qtcreator、vs2008、Eclipse、...),本文的內容都是需要理解的(儘管真正寫程式時,我們都不會直接用C++編譯器來編譯Qt程式)。

如果你對命令列比較恐懼,或許願意先看看我原來整理的這個GCC新手入門

例子一:簡單的控制檯程式

一個很簡單的例子,沒用到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 [email protected] $(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);
};

如何編譯這個程式呢?例子二中的命令還能用嗎?不妨試試: