C++ 入門指南 V2.00 - 單元 25 - 專案的檔案組成




以 MVC 模式來看我們發展的專案 (project) , M 是指 Encrypt 類別 (class) ,也就是我們已經開發好的 encrypt.cpp , V 為 Qt Creator 自動產生的 encryptwindow.ui , C 則是 encryptwindow.cpp



單元 29 才會開始整合 Encrypt 類別,我們在這個單元內先來看看 Qt Creator 自動產生的幾個檔案,下圖為新增完成的 encrypt_gui 專案資料夾



副檔名 encrpt_gui.proencrpt_gui.pro.user 是專案檔案, encryptwindow.ui 則是 GUI 介面檔案,其他還有三個程式檔案, encryptwindow.hencryptwindow.cpp 的標頭檔 (header file) ,這是 EncryptWindow 類別的實作檔案,作為控制 GUI 元件之用,而 main.cpp 則是包含 main() 函數的執行程式,我們先來看看 main() 的內容囉


001 #include "encryptwindow.h"
002 #include <QApplication>
003
004 int main(int argc, char *argv[])
005 {
006    QApplication a(argc, argv);
007    EncryptWindow w;
008    w.show();
009
010    return a.exec();
011 }

我們採粉紅色表示 Qt 程式庫的語法高亮度。

首先,引進必要的標頭檔,然後在 main() 中分別建立 QApplicationEncryptWindow 物件,並以後者呼叫 show() 來顯示視窗,最後回傳前者呼叫 exec() ,告訴作業系統 EncryptWindow 的視窗正在執行,直到使用者結束為止。


main.cpp 在後續都不會進行修改,這是 Qt 程式的基本執行程式,所有的視窗功能都由 EncryptWindow 類別設置,因此往後只需要修改擴充 EncryptWindow 即可。


至於 EncryptWindow 類別的宣告放在 encryptwindow.h ,以下為檔案內容


001 #ifndef ENCRYPTWINDOW_H
002 #define ENCRYPTWINDOW_H
003
004 #include <QMainWindow>
005
006 namespace Ui {
007 class EncryptWindow;
008 }
009
010 class EncryptWindow : public QMainWindow
011 {
012    Q_OBJECT
013
014 public:
015    explicit EncryptWindow(QWidget *parent = 0);
016    ~EncryptWindow();
017
018 private:
019    Ui::EncryptWindow *ui;
020 };
021
022 #endif // ENCRYPTWINDOW_H

第 1 行及第 2 行為前置處理器指令 (preprocessor directive) , #ifndef 用來判斷是否沒有定義 encryptwindow.h ,如果沒有定義則用 #define 定義


001 #ifndef ENCRYPTWINDOW_H
002 #define ENCRYPTWINDOW_H

這個前置處理的目的是為了避免重複引入相同的標頭檔,因為重複引入會產生編譯錯誤,簡單講就是編譯器 (compiler) 不知道要用哪一個標頭檔,然後用了 #ifndef 最後一行就要用 #endif 結束


022 #endif // ENCRYPTWINDOW_H

接下來用 namespace 宣告了命名空間 (name space) Ui


006 namespace Ui {
007 class EncryptWindow;
008 }

Ui 實際上定義在 ui_encryptwindow.h 裡,這是 Qt Creator 在編譯後自動建立的程式檔案, Ui 裡定義的 EncryptWindow 類別就是實際的視窗物件。


現在不需要太擔心 Ui::EncryptWindow 的內容,稍候我們會用 Designer 直接在 Qt Creator 的視窗中拉出 GUI 外觀,這部份 Qt Creator 會自動修改 ui_encryptwindow.h ,所以認識一下 Ui 是代表什麼就好囉!


EncryptWindow 繼承 (inherit) 自 QMainWindow ,至於繼承是寫在類別宣告的第 10 行,在 EncryptWindow 之後用冒號接父類別識別字


010 class EncryptWindow : public QMainWindow

繼承是物件導向程式設計 (object-oriented programming) 的一個特性,子類別 (subclass) 可從父類別 (superclass) 承接所有 publicprotected 的成員 (member) ,當然也可以再定義專屬於子類別的成員。這是說如果有很多個類別具有共通的特性,就可以把共通的部份抽出來設計父類別,然後用子類別去繼承,這些子類別再各自定義自己需要的成員。


「繼承」的英文原文為 inherit ,這個詞泛指從什麼得到什麼,「遺傳」的英文原文也是 inherit ,由於中文的「繼承」與「遺傳」兩者詞義有些不同,「繼承」有隱喻前者已死,後者取代前者的意思,物件導向的機制並無此意,反而比較接近「遺傳」,不過程式界沿襲「繼承」一詞已久,特此說明。

倒是要注意一個地方,現在 encryptwindow.h 中的 EncryptWindowUi::EncryptWindow 是不一樣的類別,前者是 MVC 中的 C ,後者則是 MVC 中的 V ,為了讓 C 能控制 V ,因此 EncryptWindow 需要 Ui::EncryptWindow 型態的資料成員


018 private:
019    Ui::EncryptWindow *ui;
020 };

是的,像是拉出個按鈕,然後按下按鈕要有什麼反應都透過 ui 來設定就可以了,除此之外, EncryptWindow 的第一行有


012 Q_OBJECT

Q_OBJECT 為 Qt 預設的巨集 (macro) ,因此只要建立視窗物件都會先執行這個巨集。


最後看到有兩個成員函數 (member function)


014 public:
015    explicit EncryptWindow(QWidget *parent = 0);
016    ~EncryptWindow();

建構函數 (constructor) EncryptWindow()explicit 宣告,這是避免建立 EncryptWindow 物件 (object) 時進行隱性型態轉換,而另一個有 ~ 符號的則是解構函數 (destructor) ,這是物件結束時執行的函數 (function) ,這牽涉到記憶體管理的內容,下面我們先來討論一下記憶體管理囉!


中英文術語對照


專案 project
類別 class
標頭檔 header file
前置處理器指令 preprocessor directive
編譯器 compiler
命名空間 name space
繼承 inherit
物件導向程式設計 object-oriented programming
子類別 subclass
父類別 superclass
成員 member
巨集 macro
成員函數 member function
建構函數 constructor
物件 object
解構函數 destructor
函數 function

重點整理


  1. 專案 encrypt_gui 中, M 是指 Encrypt 類別 (encrypt.cpp) , V 為 Qt Creator 自動產生的 encryptwindow.ui , C 則是 encryptwindow.cpp
  2. Qt Creator 建立專案後,所有相關檔案都會放到同一個資料夾(目錄)中。
  3. #ifndef#define#endif 等都是前置處理器指令。
  4. 關鍵字 namespace 用來宣告命名空間。
  5. 繼承是物件導向程式設計的特性之一,子類別可以承接父類別的 publicprotected 成員。
  6. Q_OBJECT 為 Qt 預設的巨集。
  7. 相對於建構函數,具有 ~ 符號的稱為解構函數。

問題與討論


  1. 為什麼 Qt 的基本執行程式 main.cpp 中要建立 QApplication 物件?
  2. 巨集是什麼?為什麼 Qt 程式要先執行 Q_OBJECT 巨集?
  3. 除了 #ifndef#define#endif 外還有其他的前置處理器指令嗎?
  4. 為什麼要宣告命名空間 Ui
  5. 繼承的意義為何?除了繼承外,物件導向程式設計還有哪些特性?

練習


  1. 承接單元 21 練習發展完成的陣列版 GuessGame 類別,重構成字串版,為此版本先設計標頭檔,將新程式寫在 exercise2501.h 中,記得先引進標準程式庫中的 string ,並將 ab 改成資料成員。
  2. 承上題,重新設計 set_game() ,將實作檔案寫在 exercise2501.cpp 中。

the end

沒有留言: